SQLiteProgram.java revision 9066cfe9886ac131c34d59ed0e2d287b0e3c0087
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
17package android.database.sqlite;
18
19import android.util.Log;
20
21/**
22 * A base class for compiled SQLite programs.
23 */
24public abstract class SQLiteProgram extends SQLiteClosable {
25    static final String TAG = "SQLiteProgram";
26
27    /** The database this program is compiled against. */
28    protected SQLiteDatabase mDatabase;
29
30    /**
31     * Native linkage, do not modify. This comes from the database and should not be modified
32     * in here or in the native code.
33     */
34    protected int nHandle = 0;
35
36    /**
37     * Native linkage, do not modify. When non-0 this holds a reference to a valid
38     * sqlite3_statement object. It is only updated by the native code, but may be
39     * checked in this class when the database lock is held to determine if there
40     * is a valid native-side program or not.
41     */
42    protected int nStatement = 0;
43
44    /**
45     * Used to find out where a cursor was allocated in case it never got
46     * released.
47     */
48    private StackTraceElement[] mStackTraceElements;
49
50    /* package */ SQLiteProgram(SQLiteDatabase db, String sql) {
51        if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
52            mStackTraceElements = new Exception().getStackTrace();
53        }
54
55        mDatabase = db;
56        db.acquireReference();
57        db.addSQLiteClosable(this);
58        this.nHandle = db.mNativeHandle;
59        compile(sql, false);
60    }
61
62    @Override
63    protected void onAllReferencesReleased() {
64        // Note that native_finalize() checks to make sure that nStatement is
65        // non-null before destroying it.
66        native_finalize();
67        mDatabase.releaseReference();
68        mDatabase.removeSQLiteClosable(this);
69    }
70
71    @Override
72    protected void onAllReferencesReleasedFromContainer(){
73        // Note that native_finalize() checks to make sure that nStatement is
74        // non-null before destroying it.
75        native_finalize();
76        mDatabase.releaseReference();
77    }
78
79    /**
80     * Returns a unique identifier for this program.
81     *
82     * @return a unique identifier for this program
83     */
84    public final int getUniqueId() {
85        return nStatement;
86    }
87
88    /**
89     * Compiles the given SQL into a SQLite byte code program using sqlite3_prepare_v2(). If
90     * this method has been called previously without a call to close and forCompilation is set
91     * to false the previous compilation will be used. Setting forceCompilation to true will
92     * always re-compile the program and should be done if you pass differing SQL strings to this
93     * method.
94     *
95     * <P>Note: this method acquires the database lock.</P>
96     *
97     * @param sql the SQL string to compile
98     * @param forceCompilation forces the SQL to be recompiled in the event that there is an
99     *  existing compiled SQL program already around
100     */
101    protected void compile(String sql, boolean forceCompilation) {
102        // Only compile if we don't have a valid statement already or the caller has
103        // explicitly requested a recompile.
104        if (nStatement == 0 || forceCompilation) {
105            mDatabase.lock();
106            try {
107                // Note that the native_compile() takes care of destroying any previously
108                // existing programs before it compiles.
109                acquireReference();
110                native_compile(sql);
111            } finally {
112                releaseReference();
113                mDatabase.unlock();
114            }
115        }
116    }
117
118    /**
119     * Bind a NULL value to this statement. The value remains bound until
120     * {@link #clearBindings} is called.
121     *
122     * @param index The 1-based index to the parameter to bind null to
123     */
124    public void bindNull(int index) {
125        acquireReference();
126        try {
127            native_bind_null(index);
128        } finally {
129            releaseReference();
130        }
131    }
132
133    /**
134     * Bind a long value to this statement. The value remains bound until
135     * {@link #clearBindings} is called.
136     *
137     * @param index The 1-based index to the parameter to bind
138     * @param value The value to bind
139     */
140    public void bindLong(int index, long value) {
141        acquireReference();
142        try {
143            native_bind_long(index, value);
144        } finally {
145            releaseReference();
146        }
147    }
148
149    /**
150     * Bind a double value to this statement. The value remains bound until
151     * {@link #clearBindings} is called.
152     *
153     * @param index The 1-based index to the parameter to bind
154     * @param value The value to bind
155     */
156    public void bindDouble(int index, double value) {
157        acquireReference();
158        try {
159            native_bind_double(index, value);
160        } finally {
161            releaseReference();
162        }
163    }
164
165    /**
166     * Bind a String value to this statement. The value remains bound until
167     * {@link #clearBindings} is called.
168     *
169     * @param index The 1-based index to the parameter to bind
170     * @param value The value to bind
171     */
172    public void bindString(int index, String value) {
173        if (value == null) {
174            throw new IllegalArgumentException("the bind value at index " + index + " is null");
175        }
176        acquireReference();
177        try {
178            native_bind_string(index, value);
179        } finally {
180            releaseReference();
181        }
182    }
183
184    /**
185     * Bind a byte array value to this statement. The value remains bound until
186     * {@link #clearBindings} is called.
187     *
188     * @param index The 1-based index to the parameter to bind
189     * @param value The value to bind
190     */
191    public void bindBlob(int index, byte[] value) {
192        if (value == null) {
193            throw new IllegalArgumentException("the bind value at index " + index + " is null");
194        }
195        acquireReference();
196        try {
197            native_bind_blob(index, value);
198        } finally {
199            releaseReference();
200        }
201    }
202
203    /**
204     * Clears all existing bindings. Unset bindings are treated as NULL.
205     */
206    public void clearBindings() {
207        acquireReference();
208        try {
209            native_clear_bindings();
210        } finally {
211            releaseReference();
212        }
213    }
214
215    /**
216     * Release this program's resources, making it invalid.
217     */
218    public void close() {
219        mDatabase.lock();
220        try {
221            releaseReference();
222        } finally {
223            mDatabase.unlock();
224        }
225    }
226
227    /**
228     * Make sure that the native resource is cleaned up.
229     */
230    @Override
231    protected void finalize() {
232        if (nStatement != 0) {
233            if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
234                String message = "Finalizing " + this +
235                    " that has not been closed";
236
237                Log.d(TAG, message + "\nThis cursor was created in:");
238                for (StackTraceElement ste : mStackTraceElements) {
239                    Log.d(TAG, "      " + ste);
240                }
241            }
242            // when in finalize() it is already removed from weakhashmap
243            // so it is safe to not removed itself from db
244            onAllReferencesReleasedFromContainer();
245        }
246    }
247
248    /**
249     * Compiles SQL into a SQLite program.
250     *
251     * <P>The database lock must be held when calling this method.
252     * @param sql The SQL to compile.
253     */
254    protected final native void native_compile(String sql);
255    protected final native void native_finalize();
256
257    protected final native void native_bind_null(int index);
258    protected final native void native_bind_long(int index, long value);
259    protected final native void native_bind_double(int index, double value);
260    protected final native void native_bind_string(int index, String value);
261    protected final native void native_bind_blob(int index, byte[] value);
262    private final native void native_clear_bindings();
263}
264
265