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.DatabaseUtils;
20 import android.os.CancellationSignal;
21 
22 import java.util.Arrays;
23 
24 /**
25  * A base class for compiled SQLite programs.
26  * <p>
27  * This class is not thread-safe.
28  * </p>
29  */
30 public abstract class SQLiteProgram extends SQLiteClosable {
31     private static final String[] EMPTY_STRING_ARRAY = new String[0];
32 
33     private final SQLiteDatabase mDatabase;
34     private final String mSql;
35     private final boolean mReadOnly;
36     private final String[] mColumnNames;
37     private final int mNumParameters;
38     private final Object[] mBindArgs;
39 
SQLiteProgram(SQLiteDatabase db, String sql, Object[] bindArgs, CancellationSignal cancellationSignalForPrepare)40     SQLiteProgram(SQLiteDatabase db, String sql, Object[] bindArgs,
41             CancellationSignal cancellationSignalForPrepare) {
42         mDatabase = db;
43         mSql = sql.trim();
44 
45         int n = DatabaseUtils.getSqlStatementType(mSql);
46         switch (n) {
47             case DatabaseUtils.STATEMENT_BEGIN:
48             case DatabaseUtils.STATEMENT_COMMIT:
49             case DatabaseUtils.STATEMENT_ABORT:
50                 mReadOnly = false;
51                 mColumnNames = EMPTY_STRING_ARRAY;
52                 mNumParameters = 0;
53                 break;
54 
55             default:
56                 boolean assumeReadOnly = (n == DatabaseUtils.STATEMENT_SELECT);
57                 SQLiteStatementInfo info = new SQLiteStatementInfo();
58                 db.getThreadSession().prepare(mSql,
59                         db.getThreadDefaultConnectionFlags(assumeReadOnly),
60                         cancellationSignalForPrepare, info);
61                 mReadOnly = info.readOnly;
62                 mColumnNames = info.columnNames;
63                 mNumParameters = info.numParameters;
64                 break;
65         }
66 
67         if (bindArgs != null && bindArgs.length > mNumParameters) {
68             throw new IllegalArgumentException("Too many bind arguments.  "
69                     + bindArgs.length + " arguments were provided but the statement needs "
70                     + mNumParameters + " arguments.");
71         }
72 
73         if (mNumParameters != 0) {
74             mBindArgs = new Object[mNumParameters];
75             if (bindArgs != null) {
76                 System.arraycopy(bindArgs, 0, mBindArgs, 0, bindArgs.length);
77             }
78         } else {
79             mBindArgs = null;
80         }
81     }
82 
getDatabase()83     final SQLiteDatabase getDatabase() {
84         return mDatabase;
85     }
86 
getSql()87     final String getSql() {
88         return mSql;
89     }
90 
getBindArgs()91     final Object[] getBindArgs() {
92         return mBindArgs;
93     }
94 
getColumnNames()95     final String[] getColumnNames() {
96         return mColumnNames;
97     }
98 
99     /** @hide */
getSession()100     protected final SQLiteSession getSession() {
101         return mDatabase.getThreadSession();
102     }
103 
104     /** @hide */
getConnectionFlags()105     protected final int getConnectionFlags() {
106         return mDatabase.getThreadDefaultConnectionFlags(mReadOnly);
107     }
108 
109     /** @hide */
onCorruption()110     protected final void onCorruption() {
111         mDatabase.onCorruption();
112     }
113 
114     /**
115      * Unimplemented.
116      * @deprecated This method is deprecated and must not be used.
117      */
118     @Deprecated
getUniqueId()119     public final int getUniqueId() {
120         return -1;
121     }
122 
123     /**
124      * Bind a NULL value to this statement. The value remains bound until
125      * {@link #clearBindings} is called.
126      *
127      * @param index The 1-based index to the parameter to bind null to
128      */
bindNull(int index)129     public void bindNull(int index) {
130         bind(index, null);
131     }
132 
133     /**
134      * Bind a long value to this statement. The value remains bound until
135      * {@link #clearBindings} is called.
136      *addToBindArgs
137      * @param index The 1-based index to the parameter to bind
138      * @param value The value to bind
139      */
bindLong(int index, long value)140     public void bindLong(int index, long value) {
141         bind(index, value);
142     }
143 
144     /**
145      * Bind a double value to this statement. The value remains bound until
146      * {@link #clearBindings} is called.
147      *
148      * @param index The 1-based index to the parameter to bind
149      * @param value The value to bind
150      */
bindDouble(int index, double value)151     public void bindDouble(int index, double value) {
152         bind(index, value);
153     }
154 
155     /**
156      * Bind a String value to this statement. The value remains bound until
157      * {@link #clearBindings} is called.
158      *
159      * @param index The 1-based index to the parameter to bind
160      * @param value The value to bind, must not be null
161      */
bindString(int index, String value)162     public void bindString(int index, String value) {
163         if (value == null) {
164             throw new IllegalArgumentException("the bind value at index " + index + " is null");
165         }
166         bind(index, value);
167     }
168 
169     /**
170      * Bind a byte array value to this statement. The value remains bound until
171      * {@link #clearBindings} is called.
172      *
173      * @param index The 1-based index to the parameter to bind
174      * @param value The value to bind, must not be null
175      */
bindBlob(int index, byte[] value)176     public void bindBlob(int index, byte[] value) {
177         if (value == null) {
178             throw new IllegalArgumentException("the bind value at index " + index + " is null");
179         }
180         bind(index, value);
181     }
182 
183     /**
184      * Clears all existing bindings. Unset bindings are treated as NULL.
185      */
clearBindings()186     public void clearBindings() {
187         if (mBindArgs != null) {
188             Arrays.fill(mBindArgs, null);
189         }
190     }
191 
192     /**
193      * Given an array of String bindArgs, this method binds all of them in one single call.
194      *
195      * @param bindArgs the String array of bind args, none of which must be null.
196      */
bindAllArgsAsStrings(String[] bindArgs)197     public void bindAllArgsAsStrings(String[] bindArgs) {
198         if (bindArgs != null) {
199             for (int i = bindArgs.length; i != 0; i--) {
200                 bindString(i, bindArgs[i - 1]);
201             }
202         }
203     }
204 
205     @Override
onAllReferencesReleased()206     protected void onAllReferencesReleased() {
207         clearBindings();
208     }
209 
bind(int index, Object value)210     private void bind(int index, Object value) {
211         if (index < 1 || index > mNumParameters) {
212             throw new IllegalArgumentException("Cannot bind argument at index "
213                     + index + " because the index is out of range.  "
214                     + "The statement has " + mNumParameters + " parameters.");
215         }
216         mBindArgs[index - 1] = value;
217     }
218 }
219