Home | History | Annotate | Download | only in sqlite
      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.util.Log;
     20 
     21 /**
     22  * A base class for compiled SQLite programs.
     23  *
     24  * SQLiteProgram is not internally synchronized so code using a SQLiteProgram from multiple
     25  * threads should perform its own synchronization when using the SQLiteProgram.
     26  */
     27 public abstract class SQLiteProgram extends SQLiteClosable {
     28 
     29     private static final String TAG = "SQLiteProgram";
     30 
     31     /** The database this program is compiled against.
     32      * @deprecated do not use this
     33      */
     34     @Deprecated
     35     protected SQLiteDatabase mDatabase;
     36 
     37     /** The SQL used to create this query */
     38     /* package */ final String mSql;
     39 
     40     /**
     41      * Native linkage, do not modify. This comes from the database and should not be modified
     42      * in here or in the native code.
     43      * @deprecated do not use this
     44      */
     45     @Deprecated
     46     protected int nHandle = 0;
     47 
     48     /**
     49      * the SQLiteCompiledSql object for the given sql statement.
     50      */
     51     private SQLiteCompiledSql mCompiledSql;
     52 
     53     /**
     54      * SQLiteCompiledSql statement id is populated with the corresponding object from the above
     55      * member. This member is used by the native_bind_* methods
     56      * @deprecated do not use this
     57      */
     58     @Deprecated
     59     protected int nStatement = 0;
     60 
     61     /* package */ SQLiteProgram(SQLiteDatabase db, String sql) {
     62         mDatabase = db;
     63         mSql = sql.trim();
     64         db.acquireReference();
     65         db.addSQLiteClosable(this);
     66         this.nHandle = db.mNativeHandle;
     67 
     68         // only cache CRUD statements
     69         String prefixSql = mSql.substring(0, 6);
     70         if (!prefixSql.equalsIgnoreCase("INSERT") && !prefixSql.equalsIgnoreCase("UPDATE") &&
     71                 !prefixSql.equalsIgnoreCase("REPLAC") &&
     72                 !prefixSql.equalsIgnoreCase("DELETE") && !prefixSql.equalsIgnoreCase("SELECT")) {
     73             mCompiledSql = new SQLiteCompiledSql(db, sql);
     74             nStatement = mCompiledSql.nStatement;
     75             // since it is not in the cache, no need to acquire() it.
     76             return;
     77         }
     78 
     79         // it is not pragma
     80         mCompiledSql = db.getCompiledStatementForSql(sql);
     81         if (mCompiledSql == null) {
     82             // create a new compiled-sql obj
     83             mCompiledSql = new SQLiteCompiledSql(db, sql);
     84 
     85             // add it to the cache of compiled-sqls
     86             // but before adding it and thus making it available for anyone else to use it,
     87             // make sure it is acquired by me.
     88             mCompiledSql.acquire();
     89             db.addToCompiledQueries(sql, mCompiledSql);
     90             if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
     91                 Log.v(TAG, "Created DbObj (id#" + mCompiledSql.nStatement +
     92                         ") for sql: " + sql);
     93             }
     94         } else {
     95             // it is already in compiled-sql cache.
     96             // try to acquire the object.
     97             if (!mCompiledSql.acquire()) {
     98                 int last = mCompiledSql.nStatement;
     99                 // the SQLiteCompiledSql in cache is in use by some other SQLiteProgram object.
    100                 // we can't have two different SQLiteProgam objects can't share the same
    101                 // CompiledSql object. create a new one.
    102                 // finalize it when I am done with it in "this" object.
    103                 mCompiledSql = new SQLiteCompiledSql(db, sql);
    104                 if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
    105                     Log.v(TAG, "** possible bug ** Created NEW DbObj (id#" +
    106                             mCompiledSql.nStatement +
    107                             ") because the previously created DbObj (id#" + last +
    108                             ") was not released for sql:" + sql);
    109                 }
    110                 // since it is not in the cache, no need to acquire() it.
    111             }
    112         }
    113         nStatement = mCompiledSql.nStatement;
    114     }
    115 
    116     @Override
    117     protected void onAllReferencesReleased() {
    118         releaseCompiledSqlIfNotInCache();
    119         mDatabase.releaseReference();
    120         mDatabase.removeSQLiteClosable(this);
    121     }
    122 
    123     @Override
    124     protected void onAllReferencesReleasedFromContainer() {
    125         releaseCompiledSqlIfNotInCache();
    126         mDatabase.releaseReference();
    127     }
    128 
    129     private void releaseCompiledSqlIfNotInCache() {
    130         if (mCompiledSql == null) {
    131             return;
    132         }
    133         synchronized(mDatabase.mCompiledQueries) {
    134             if (!mDatabase.mCompiledQueries.containsValue(mCompiledSql)) {
    135                 // it is NOT in compiled-sql cache. i.e., responsibility of
    136                 // releasing this statement is on me.
    137                 mCompiledSql.releaseSqlStatement();
    138                 mCompiledSql = null;
    139                 nStatement = 0;
    140             } else {
    141                 // it is in compiled-sql cache. reset its CompiledSql#mInUse flag
    142                 mCompiledSql.release();
    143             }
    144         }
    145     }
    146 
    147     /**
    148      * Returns a unique identifier for this program.
    149      *
    150      * @return a unique identifier for this program
    151      */
    152     public final int getUniqueId() {
    153         return nStatement;
    154     }
    155 
    156     /* package */ String getSqlString() {
    157         return mSql;
    158     }
    159 
    160     /**
    161      * @deprecated This method is deprecated and must not be used.
    162      *
    163      * @param sql the SQL string to compile
    164      * @param forceCompilation forces the SQL to be recompiled in the event that there is an
    165      *  existing compiled SQL program already around
    166      */
    167     @Deprecated
    168     protected void compile(String sql, boolean forceCompilation) {
    169         // TODO is there a need for this?
    170     }
    171 
    172     /**
    173      * Bind a NULL value to this statement. The value remains bound until
    174      * {@link #clearBindings} is called.
    175      *
    176      * @param index The 1-based index to the parameter to bind null to
    177      */
    178     public void bindNull(int index) {
    179         if (!mDatabase.isOpen()) {
    180             throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
    181         }
    182         acquireReference();
    183         try {
    184             native_bind_null(index);
    185         } finally {
    186             releaseReference();
    187         }
    188     }
    189 
    190     /**
    191      * Bind a long value to this statement. The value remains bound until
    192      * {@link #clearBindings} is called.
    193      *
    194      * @param index The 1-based index to the parameter to bind
    195      * @param value The value to bind
    196      */
    197     public void bindLong(int index, long value) {
    198         if (!mDatabase.isOpen()) {
    199             throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
    200         }
    201         acquireReference();
    202         try {
    203             native_bind_long(index, value);
    204         } finally {
    205             releaseReference();
    206         }
    207     }
    208 
    209     /**
    210      * Bind a double value to this statement. The value remains bound until
    211      * {@link #clearBindings} is called.
    212      *
    213      * @param index The 1-based index to the parameter to bind
    214      * @param value The value to bind
    215      */
    216     public void bindDouble(int index, double value) {
    217         if (!mDatabase.isOpen()) {
    218             throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
    219         }
    220         acquireReference();
    221         try {
    222             native_bind_double(index, value);
    223         } finally {
    224             releaseReference();
    225         }
    226     }
    227 
    228     /**
    229      * Bind a String value to this statement. The value remains bound until
    230      * {@link #clearBindings} is called.
    231      *
    232      * @param index The 1-based index to the parameter to bind
    233      * @param value The value to bind
    234      */
    235     public void bindString(int index, String value) {
    236         if (value == null) {
    237             throw new IllegalArgumentException("the bind value at index " + index + " is null");
    238         }
    239         if (!mDatabase.isOpen()) {
    240             throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
    241         }
    242         acquireReference();
    243         try {
    244             native_bind_string(index, value);
    245         } finally {
    246             releaseReference();
    247         }
    248     }
    249 
    250     /**
    251      * Bind a byte array value to this statement. The value remains bound until
    252      * {@link #clearBindings} is called.
    253      *
    254      * @param index The 1-based index to the parameter to bind
    255      * @param value The value to bind
    256      */
    257     public void bindBlob(int index, byte[] value) {
    258         if (value == null) {
    259             throw new IllegalArgumentException("the bind value at index " + index + " is null");
    260         }
    261         if (!mDatabase.isOpen()) {
    262             throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
    263         }
    264         acquireReference();
    265         try {
    266             native_bind_blob(index, value);
    267         } finally {
    268             releaseReference();
    269         }
    270     }
    271 
    272     /**
    273      * Clears all existing bindings. Unset bindings are treated as NULL.
    274      */
    275     public void clearBindings() {
    276         if (!mDatabase.isOpen()) {
    277             throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
    278         }
    279         acquireReference();
    280         try {
    281             native_clear_bindings();
    282         } finally {
    283             releaseReference();
    284         }
    285     }
    286 
    287     /**
    288      * Release this program's resources, making it invalid.
    289      */
    290     public void close() {
    291         if (!mDatabase.isOpen()) {
    292             return;
    293         }
    294         mDatabase.lock();
    295         try {
    296             releaseReference();
    297         } finally {
    298             mDatabase.unlock();
    299         }
    300     }
    301 
    302     /**
    303      * @deprecated This method is deprecated and must not be used.
    304      * Compiles SQL into a SQLite program.
    305      *
    306      * <P>The database lock must be held when calling this method.
    307      * @param sql The SQL to compile.
    308      */
    309     @Deprecated
    310     protected final native void native_compile(String sql);
    311 
    312     /**
    313      * @deprecated This method is deprecated and must not be used.
    314      */
    315     @Deprecated
    316     protected final native void native_finalize();
    317 
    318     protected final native void native_bind_null(int index);
    319     protected final native void native_bind_long(int index, long value);
    320     protected final native void native_bind_double(int index, double value);
    321     protected final native void native_bind_string(int index, String value);
    322     protected final native void native_bind_blob(int index, byte[] value);
    323     private final native void native_clear_bindings();
    324 }
    325 
    326