blob: a8ba9a36ad2ec47ccd81570941fe89ccbf721df7 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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;
18
19import org.apache.commons.codec.binary.Hex;
20
21import android.content.ContentValues;
22import android.content.Context;
Fred Quintana89437372009-05-15 15:10:40 -070023import android.content.OperationApplicationException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.database.sqlite.SQLiteAbortException;
25import android.database.sqlite.SQLiteConstraintException;
26import android.database.sqlite.SQLiteDatabase;
27import android.database.sqlite.SQLiteDatabaseCorruptException;
28import android.database.sqlite.SQLiteDiskIOException;
29import android.database.sqlite.SQLiteException;
30import android.database.sqlite.SQLiteFullException;
31import android.database.sqlite.SQLiteProgram;
32import android.database.sqlite.SQLiteStatement;
33import android.os.Parcel;
Bjorn Bringerta006b4722010-04-14 14:43:26 +010034import android.os.ParcelFileDescriptor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.text.TextUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.util.Log;
37
38import java.io.FileNotFoundException;
39import java.io.PrintStream;
40import java.text.Collator;
41import java.util.HashMap;
42import java.util.Map;
43
44/**
45 * Static utility methods for dealing with databases and {@link Cursor}s.
46 */
47public class DatabaseUtils {
48 private static final String TAG = "DatabaseUtils";
49
50 private static final boolean DEBUG = false;
Joe Onorato43a17652011-04-06 19:22:23 -070051 private static final boolean LOCAL_LOGV = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052
53 private static final String[] countProjection = new String[]{"count(*)"};
54
Vasu Norice38b982010-07-22 13:57:13 -070055 /** One of the values returned by {@link #getSqlStatementType(String)}. */
56 public static final int STATEMENT_SELECT = 1;
57 /** One of the values returned by {@link #getSqlStatementType(String)}. */
58 public static final int STATEMENT_UPDATE = 2;
59 /** One of the values returned by {@link #getSqlStatementType(String)}. */
60 public static final int STATEMENT_ATTACH = 3;
61 /** One of the values returned by {@link #getSqlStatementType(String)}. */
62 public static final int STATEMENT_BEGIN = 4;
63 /** One of the values returned by {@link #getSqlStatementType(String)}. */
64 public static final int STATEMENT_COMMIT = 5;
65 /** One of the values returned by {@link #getSqlStatementType(String)}. */
66 public static final int STATEMENT_ABORT = 6;
67 /** One of the values returned by {@link #getSqlStatementType(String)}. */
Vasu Nori4e874ed2010-09-15 18:40:49 -070068 public static final int STATEMENT_PRAGMA = 7;
69 /** One of the values returned by {@link #getSqlStatementType(String)}. */
70 public static final int STATEMENT_DDL = 8;
71 /** One of the values returned by {@link #getSqlStatementType(String)}. */
72 public static final int STATEMENT_UNPREPARED = 9;
73 /** One of the values returned by {@link #getSqlStatementType(String)}. */
74 public static final int STATEMENT_OTHER = 99;
Vasu Norice38b982010-07-22 13:57:13 -070075
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 /**
77 * Special function for writing an exception result at the header of
78 * a parcel, to be used when returning an exception from a transaction.
79 * exception will be re-thrown by the function in another process
80 * @param reply Parcel to write to
81 * @param e The Exception to be written.
82 * @see Parcel#writeNoException
83 * @see Parcel#writeException
84 */
85 public static final void writeExceptionToParcel(Parcel reply, Exception e) {
86 int code = 0;
87 boolean logException = true;
88 if (e instanceof FileNotFoundException) {
89 code = 1;
90 logException = false;
91 } else if (e instanceof IllegalArgumentException) {
92 code = 2;
93 } else if (e instanceof UnsupportedOperationException) {
94 code = 3;
95 } else if (e instanceof SQLiteAbortException) {
96 code = 4;
97 } else if (e instanceof SQLiteConstraintException) {
98 code = 5;
99 } else if (e instanceof SQLiteDatabaseCorruptException) {
100 code = 6;
101 } else if (e instanceof SQLiteFullException) {
102 code = 7;
103 } else if (e instanceof SQLiteDiskIOException) {
104 code = 8;
105 } else if (e instanceof SQLiteException) {
106 code = 9;
Fred Quintana89437372009-05-15 15:10:40 -0700107 } else if (e instanceof OperationApplicationException) {
108 code = 10;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 } else {
110 reply.writeException(e);
111 Log.e(TAG, "Writing exception to parcel", e);
112 return;
113 }
114 reply.writeInt(code);
115 reply.writeString(e.getMessage());
116
117 if (logException) {
118 Log.e(TAG, "Writing exception to parcel", e);
119 }
120 }
121
122 /**
123 * Special function for reading an exception result from the header of
124 * a parcel, to be used after receiving the result of a transaction. This
125 * will throw the exception for you if it had been written to the Parcel,
126 * otherwise return and let you read the normal result data from the Parcel.
127 * @param reply Parcel to read from
128 * @see Parcel#writeNoException
129 * @see Parcel#readException
130 */
131 public static final void readExceptionFromParcel(Parcel reply) {
Brad Fitzpatrick5b747192010-07-12 11:05:38 -0700132 int code = reply.readExceptionCode();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 if (code == 0) return;
134 String msg = reply.readString();
135 DatabaseUtils.readExceptionFromParcel(reply, msg, code);
136 }
137
138 public static void readExceptionWithFileNotFoundExceptionFromParcel(
139 Parcel reply) throws FileNotFoundException {
Brad Fitzpatrick5b747192010-07-12 11:05:38 -0700140 int code = reply.readExceptionCode();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 if (code == 0) return;
142 String msg = reply.readString();
143 if (code == 1) {
144 throw new FileNotFoundException(msg);
145 } else {
146 DatabaseUtils.readExceptionFromParcel(reply, msg, code);
147 }
148 }
149
Fred Quintana89437372009-05-15 15:10:40 -0700150 public static void readExceptionWithOperationApplicationExceptionFromParcel(
151 Parcel reply) throws OperationApplicationException {
Brad Fitzpatrick5b747192010-07-12 11:05:38 -0700152 int code = reply.readExceptionCode();
Fred Quintana89437372009-05-15 15:10:40 -0700153 if (code == 0) return;
154 String msg = reply.readString();
155 if (code == 10) {
156 throw new OperationApplicationException(msg);
157 } else {
158 DatabaseUtils.readExceptionFromParcel(reply, msg, code);
159 }
160 }
161
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 private static final void readExceptionFromParcel(Parcel reply, String msg, int code) {
163 switch (code) {
164 case 2:
165 throw new IllegalArgumentException(msg);
166 case 3:
167 throw new UnsupportedOperationException(msg);
168 case 4:
169 throw new SQLiteAbortException(msg);
170 case 5:
171 throw new SQLiteConstraintException(msg);
172 case 6:
173 throw new SQLiteDatabaseCorruptException(msg);
174 case 7:
175 throw new SQLiteFullException(msg);
176 case 8:
177 throw new SQLiteDiskIOException(msg);
178 case 9:
179 throw new SQLiteException(msg);
180 default:
181 reply.readException(code, msg);
182 }
183 }
184
185 /**
186 * Binds the given Object to the given SQLiteProgram using the proper
187 * typing. For example, bind numbers as longs/doubles, and everything else
188 * as a string by call toString() on it.
189 *
190 * @param prog the program to bind the object to
191 * @param index the 1-based index to bind at
192 * @param value the value to bind
193 */
194 public static void bindObjectToProgram(SQLiteProgram prog, int index,
195 Object value) {
196 if (value == null) {
197 prog.bindNull(index);
198 } else if (value instanceof Double || value instanceof Float) {
199 prog.bindDouble(index, ((Number)value).doubleValue());
200 } else if (value instanceof Number) {
201 prog.bindLong(index, ((Number)value).longValue());
202 } else if (value instanceof Boolean) {
203 Boolean bool = (Boolean)value;
204 if (bool) {
205 prog.bindLong(index, 1);
206 } else {
207 prog.bindLong(index, 0);
208 }
209 } else if (value instanceof byte[]){
210 prog.bindBlob(index, (byte[]) value);
211 } else {
212 prog.bindString(index, value.toString());
213 }
214 }
215
216 /**
Vasu Nori8b0dd7d2010-05-18 11:54:31 -0700217 * Returns data type of the given object's value.
218 *<p>
219 * Returned values are
220 * <ul>
221 * <li>{@link Cursor#FIELD_TYPE_NULL}</li>
222 * <li>{@link Cursor#FIELD_TYPE_INTEGER}</li>
223 * <li>{@link Cursor#FIELD_TYPE_FLOAT}</li>
224 * <li>{@link Cursor#FIELD_TYPE_STRING}</li>
225 * <li>{@link Cursor#FIELD_TYPE_BLOB}</li>
226 *</ul>
227 *</p>
228 *
229 * @param obj the object whose value type is to be returned
230 * @return object value type
231 * @hide
232 */
233 public static int getTypeOfObject(Object obj) {
234 if (obj == null) {
235 return Cursor.FIELD_TYPE_NULL;
236 } else if (obj instanceof byte[]) {
237 return Cursor.FIELD_TYPE_BLOB;
238 } else if (obj instanceof Float || obj instanceof Double) {
239 return Cursor.FIELD_TYPE_FLOAT;
Jeff Brown80e7b802011-10-12 17:42:41 -0700240 } else if (obj instanceof Long || obj instanceof Integer
241 || obj instanceof Short || obj instanceof Byte) {
Vasu Nori8b0dd7d2010-05-18 11:54:31 -0700242 return Cursor.FIELD_TYPE_INTEGER;
243 } else {
244 return Cursor.FIELD_TYPE_STRING;
245 }
246 }
247
248 /**
Jeff Brown80e7b802011-10-12 17:42:41 -0700249 * Fills the specified cursor window by iterating over the contents of the cursor.
250 * The window is filled until the cursor is exhausted or the window runs out
251 * of space.
252 *
253 * The original position of the cursor is left unchanged by this operation.
254 *
255 * @param cursor The cursor that contains the data to put in the window.
256 * @param position The start position for filling the window.
257 * @param window The window to fill.
258 * @hide
259 */
260 public static void cursorFillWindow(final Cursor cursor,
261 int position, final CursorWindow window) {
262 if (position < 0 || position >= cursor.getCount()) {
263 return;
264 }
265 window.acquireReference();
266 try {
267 final int oldPos = cursor.getPosition();
268 final int numColumns = cursor.getColumnCount();
269 window.clear();
270 window.setStartPosition(position);
271 window.setNumColumns(numColumns);
272 if (cursor.moveToPosition(position)) {
273 do {
274 if (!window.allocRow()) {
275 break;
276 }
277 for (int i = 0; i < numColumns; i++) {
278 final int type = cursor.getType(i);
279 final boolean success;
280 switch (type) {
281 case Cursor.FIELD_TYPE_NULL:
282 success = window.putNull(position, i);
283 break;
284
285 case Cursor.FIELD_TYPE_INTEGER:
286 success = window.putLong(cursor.getLong(i), position, i);
287 break;
288
289 case Cursor.FIELD_TYPE_FLOAT:
290 success = window.putDouble(cursor.getDouble(i), position, i);
291 break;
292
293 case Cursor.FIELD_TYPE_BLOB: {
294 final byte[] value = cursor.getBlob(i);
295 success = value != null ? window.putBlob(value, position, i)
296 : window.putNull(position, i);
297 break;
298 }
299
300 default: // assume value is convertible to String
301 case Cursor.FIELD_TYPE_STRING: {
302 final String value = cursor.getString(i);
303 success = value != null ? window.putString(value, position, i)
304 : window.putNull(position, i);
305 break;
306 }
307 }
308 if (!success) {
309 window.freeLastRow();
310 break;
311 }
312 }
313 position += 1;
314 } while (cursor.moveToNext());
315 }
316 cursor.moveToPosition(oldPos);
317 } catch (IllegalStateException e){
318 // simply ignore it
319 } finally {
320 window.releaseReference();
321 }
322 }
323
324 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325 * Appends an SQL string to the given StringBuilder, including the opening
326 * and closing single quotes. Any single quotes internal to sqlString will
327 * be escaped.
328 *
329 * This method is deprecated because we want to encourage everyone
330 * to use the "?" binding form. However, when implementing a
331 * ContentProvider, one may want to add WHERE clauses that were
332 * not provided by the caller. Since "?" is a positional form,
333 * using it in this case could break the caller because the
334 * indexes would be shifted to accomodate the ContentProvider's
335 * internal bindings. In that case, it may be necessary to
336 * construct a WHERE clause manually. This method is useful for
337 * those cases.
338 *
339 * @param sb the StringBuilder that the SQL string will be appended to
340 * @param sqlString the raw string to be appended, which may contain single
341 * quotes
342 */
343 public static void appendEscapedSQLString(StringBuilder sb, String sqlString) {
344 sb.append('\'');
345 if (sqlString.indexOf('\'') != -1) {
346 int length = sqlString.length();
347 for (int i = 0; i < length; i++) {
348 char c = sqlString.charAt(i);
349 if (c == '\'') {
350 sb.append('\'');
351 }
352 sb.append(c);
353 }
354 } else
355 sb.append(sqlString);
356 sb.append('\'');
357 }
Fred Quintana22f71142009-03-24 20:10:17 -0700358
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 /**
360 * SQL-escape a string.
361 */
362 public static String sqlEscapeString(String value) {
363 StringBuilder escaper = new StringBuilder();
364
365 DatabaseUtils.appendEscapedSQLString(escaper, value);
366
367 return escaper.toString();
368 }
369
370 /**
371 * Appends an Object to an SQL string with the proper escaping, etc.
372 */
373 public static final void appendValueToSql(StringBuilder sql, Object value) {
374 if (value == null) {
375 sql.append("NULL");
376 } else if (value instanceof Boolean) {
377 Boolean bool = (Boolean)value;
378 if (bool) {
379 sql.append('1');
380 } else {
381 sql.append('0');
382 }
383 } else {
384 appendEscapedSQLString(sql, value.toString());
385 }
386 }
Fred Quintana22f71142009-03-24 20:10:17 -0700387
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 /**
389 * Concatenates two SQL WHERE clauses, handling empty or null values.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390 */
391 public static String concatenateWhere(String a, String b) {
392 if (TextUtils.isEmpty(a)) {
393 return b;
394 }
395 if (TextUtils.isEmpty(b)) {
396 return a;
397 }
Fred Quintana22f71142009-03-24 20:10:17 -0700398
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 return "(" + a + ") AND (" + b + ")";
400 }
Fred Quintana22f71142009-03-24 20:10:17 -0700401
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 /**
Fred Quintana22f71142009-03-24 20:10:17 -0700403 * return the collation key
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 * @param name
405 * @return the collation key
406 */
407 public static String getCollationKey(String name) {
408 byte [] arr = getCollationKeyInBytes(name);
409 try {
410 return new String(arr, 0, getKeyLen(arr), "ISO8859_1");
411 } catch (Exception ex) {
412 return "";
413 }
414 }
Fred Quintana22f71142009-03-24 20:10:17 -0700415
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 /**
417 * return the collation key in hex format
418 * @param name
419 * @return the collation key in hex format
420 */
421 public static String getHexCollationKey(String name) {
422 byte [] arr = getCollationKeyInBytes(name);
423 char[] keys = Hex.encodeHex(arr);
424 return new String(keys, 0, getKeyLen(arr) * 2);
425 }
Fred Quintana22f71142009-03-24 20:10:17 -0700426
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427 private static int getKeyLen(byte[] arr) {
428 if (arr[arr.length - 1] != 0) {
429 return arr.length;
430 } else {
431 // remove zero "termination"
432 return arr.length-1;
433 }
434 }
Fred Quintana22f71142009-03-24 20:10:17 -0700435
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436 private static byte[] getCollationKeyInBytes(String name) {
437 if (mColl == null) {
438 mColl = Collator.getInstance();
439 mColl.setStrength(Collator.PRIMARY);
440 }
Fred Quintana22f71142009-03-24 20:10:17 -0700441 return mColl.getCollationKey(name).toByteArray();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 }
Fred Quintana22f71142009-03-24 20:10:17 -0700443
444 private static Collator mColl = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 /**
446 * Prints the contents of a Cursor to System.out. The position is restored
447 * after printing.
448 *
449 * @param cursor the cursor to print
450 */
451 public static void dumpCursor(Cursor cursor) {
452 dumpCursor(cursor, System.out);
453 }
454
455 /**
456 * Prints the contents of a Cursor to a PrintSteam. The position is restored
457 * after printing.
458 *
459 * @param cursor the cursor to print
460 * @param stream the stream to print to
461 */
462 public static void dumpCursor(Cursor cursor, PrintStream stream) {
463 stream.println(">>>>> Dumping cursor " + cursor);
464 if (cursor != null) {
465 int startPos = cursor.getPosition();
466
467 cursor.moveToPosition(-1);
468 while (cursor.moveToNext()) {
469 dumpCurrentRow(cursor, stream);
470 }
471 cursor.moveToPosition(startPos);
472 }
473 stream.println("<<<<<");
474 }
475
476 /**
477 * Prints the contents of a Cursor to a StringBuilder. The position
478 * is restored after printing.
479 *
480 * @param cursor the cursor to print
481 * @param sb the StringBuilder to print to
482 */
483 public static void dumpCursor(Cursor cursor, StringBuilder sb) {
484 sb.append(">>>>> Dumping cursor " + cursor + "\n");
485 if (cursor != null) {
486 int startPos = cursor.getPosition();
487
488 cursor.moveToPosition(-1);
489 while (cursor.moveToNext()) {
490 dumpCurrentRow(cursor, sb);
491 }
492 cursor.moveToPosition(startPos);
493 }
494 sb.append("<<<<<\n");
495 }
496
497 /**
498 * Prints the contents of a Cursor to a String. The position is restored
499 * after printing.
500 *
501 * @param cursor the cursor to print
502 * @return a String that contains the dumped cursor
503 */
504 public static String dumpCursorToString(Cursor cursor) {
505 StringBuilder sb = new StringBuilder();
506 dumpCursor(cursor, sb);
507 return sb.toString();
508 }
509
510 /**
511 * Prints the contents of a Cursor's current row to System.out.
512 *
513 * @param cursor the cursor to print from
514 */
515 public static void dumpCurrentRow(Cursor cursor) {
516 dumpCurrentRow(cursor, System.out);
517 }
518
519 /**
520 * Prints the contents of a Cursor's current row to a PrintSteam.
521 *
522 * @param cursor the cursor to print
523 * @param stream the stream to print to
524 */
525 public static void dumpCurrentRow(Cursor cursor, PrintStream stream) {
526 String[] cols = cursor.getColumnNames();
527 stream.println("" + cursor.getPosition() + " {");
528 int length = cols.length;
529 for (int i = 0; i< length; i++) {
530 String value;
531 try {
532 value = cursor.getString(i);
533 } catch (SQLiteException e) {
534 // assume that if the getString threw this exception then the column is not
535 // representable by a string, e.g. it is a BLOB.
536 value = "<unprintable>";
537 }
538 stream.println(" " + cols[i] + '=' + value);
539 }
540 stream.println("}");
541 }
542
543 /**
544 * Prints the contents of a Cursor's current row to a StringBuilder.
545 *
546 * @param cursor the cursor to print
547 * @param sb the StringBuilder to print to
548 */
549 public static void dumpCurrentRow(Cursor cursor, StringBuilder sb) {
550 String[] cols = cursor.getColumnNames();
551 sb.append("" + cursor.getPosition() + " {\n");
552 int length = cols.length;
553 for (int i = 0; i < length; i++) {
554 String value;
555 try {
556 value = cursor.getString(i);
557 } catch (SQLiteException e) {
558 // assume that if the getString threw this exception then the column is not
559 // representable by a string, e.g. it is a BLOB.
560 value = "<unprintable>";
561 }
562 sb.append(" " + cols[i] + '=' + value + "\n");
563 }
564 sb.append("}\n");
565 }
566
567 /**
568 * Dump the contents of a Cursor's current row to a String.
569 *
570 * @param cursor the cursor to print
571 * @return a String that contains the dumped cursor row
572 */
573 public static String dumpCurrentRowToString(Cursor cursor) {
574 StringBuilder sb = new StringBuilder();
575 dumpCurrentRow(cursor, sb);
576 return sb.toString();
577 }
578
579 /**
580 * Reads a String out of a field in a Cursor and writes it to a Map.
581 *
582 * @param cursor The cursor to read from
583 * @param field The TEXT field to read
584 * @param values The {@link ContentValues} to put the value into, with the field as the key
585 */
586 public static void cursorStringToContentValues(Cursor cursor, String field,
587 ContentValues values) {
588 cursorStringToContentValues(cursor, field, values, field);
589 }
590
591 /**
592 * Reads a String out of a field in a Cursor and writes it to an InsertHelper.
593 *
594 * @param cursor The cursor to read from
595 * @param field The TEXT field to read
596 * @param inserter The InsertHelper to bind into
597 * @param index the index of the bind entry in the InsertHelper
598 */
599 public static void cursorStringToInsertHelper(Cursor cursor, String field,
600 InsertHelper inserter, int index) {
601 inserter.bind(index, cursor.getString(cursor.getColumnIndexOrThrow(field)));
602 }
603
604 /**
605 * Reads a String out of a field in a Cursor and writes it to a Map.
606 *
607 * @param cursor The cursor to read from
608 * @param field The TEXT field to read
609 * @param values The {@link ContentValues} to put the value into, with the field as the key
610 * @param key The key to store the value with in the map
611 */
612 public static void cursorStringToContentValues(Cursor cursor, String field,
613 ContentValues values, String key) {
614 values.put(key, cursor.getString(cursor.getColumnIndexOrThrow(field)));
615 }
616
617 /**
618 * Reads an Integer out of a field in a Cursor and writes it to a Map.
619 *
620 * @param cursor The cursor to read from
621 * @param field The INTEGER field to read
622 * @param values The {@link ContentValues} to put the value into, with the field as the key
623 */
624 public static void cursorIntToContentValues(Cursor cursor, String field, ContentValues values) {
625 cursorIntToContentValues(cursor, field, values, field);
626 }
627
628 /**
629 * Reads a Integer out of a field in a Cursor and writes it to a Map.
630 *
631 * @param cursor The cursor to read from
632 * @param field The INTEGER field to read
633 * @param values The {@link ContentValues} to put the value into, with the field as the key
634 * @param key The key to store the value with in the map
635 */
636 public static void cursorIntToContentValues(Cursor cursor, String field, ContentValues values,
637 String key) {
638 int colIndex = cursor.getColumnIndex(field);
639 if (!cursor.isNull(colIndex)) {
640 values.put(key, cursor.getInt(colIndex));
641 } else {
642 values.put(key, (Integer) null);
643 }
644 }
645
646 /**
647 * Reads a Long out of a field in a Cursor and writes it to a Map.
648 *
649 * @param cursor The cursor to read from
650 * @param field The INTEGER field to read
651 * @param values The {@link ContentValues} to put the value into, with the field as the key
652 */
653 public static void cursorLongToContentValues(Cursor cursor, String field, ContentValues values)
654 {
655 cursorLongToContentValues(cursor, field, values, field);
656 }
657
658 /**
659 * Reads a Long out of a field in a Cursor and writes it to a Map.
660 *
661 * @param cursor The cursor to read from
662 * @param field The INTEGER field to read
663 * @param values The {@link ContentValues} to put the value into
664 * @param key The key to store the value with in the map
665 */
666 public static void cursorLongToContentValues(Cursor cursor, String field, ContentValues values,
667 String key) {
668 int colIndex = cursor.getColumnIndex(field);
669 if (!cursor.isNull(colIndex)) {
670 Long value = Long.valueOf(cursor.getLong(colIndex));
671 values.put(key, value);
672 } else {
673 values.put(key, (Long) null);
674 }
675 }
676
677 /**
678 * Reads a Double out of a field in a Cursor and writes it to a Map.
679 *
680 * @param cursor The cursor to read from
681 * @param field The REAL field to read
682 * @param values The {@link ContentValues} to put the value into
683 */
684 public static void cursorDoubleToCursorValues(Cursor cursor, String field, ContentValues values)
685 {
686 cursorDoubleToContentValues(cursor, field, values, field);
687 }
688
689 /**
690 * Reads a Double out of a field in a Cursor and writes it to a Map.
691 *
692 * @param cursor The cursor to read from
693 * @param field The REAL field to read
694 * @param values The {@link ContentValues} to put the value into
695 * @param key The key to store the value with in the map
696 */
697 public static void cursorDoubleToContentValues(Cursor cursor, String field,
698 ContentValues values, String key) {
699 int colIndex = cursor.getColumnIndex(field);
700 if (!cursor.isNull(colIndex)) {
701 values.put(key, cursor.getDouble(colIndex));
702 } else {
703 values.put(key, (Double) null);
704 }
705 }
706
707 /**
708 * Read the entire contents of a cursor row and store them in a ContentValues.
709 *
710 * @param cursor the cursor to read from.
711 * @param values the {@link ContentValues} to put the row into.
712 */
713 public static void cursorRowToContentValues(Cursor cursor, ContentValues values) {
714 AbstractWindowedCursor awc =
715 (cursor instanceof AbstractWindowedCursor) ? (AbstractWindowedCursor) cursor : null;
716
717 String[] columns = cursor.getColumnNames();
718 int length = columns.length;
719 for (int i = 0; i < length; i++) {
720 if (awc != null && awc.isBlob(i)) {
721 values.put(columns[i], cursor.getBlob(i));
722 } else {
723 values.put(columns[i], cursor.getString(i));
724 }
725 }
726 }
727
728 /**
Jeff Brown650de3d2011-10-27 14:52:28 -0700729 * Picks a start position for {@link Cursor#fillWindow} such that the
730 * window will contain the requested row and a useful range of rows
731 * around it.
732 *
733 * When the data set is too large to fit in a cursor window, seeking the
734 * cursor can become a very expensive operation since we have to run the
735 * query again when we move outside the bounds of the current window.
736 *
737 * We try to choose a start position for the cursor window such that
738 * 1/3 of the window's capacity is used to hold rows before the requested
739 * position and 2/3 of the window's capacity is used to hold rows after the
740 * requested position.
741 *
742 * @param cursorPosition The row index of the row we want to get.
743 * @param cursorWindowCapacity The estimated number of rows that can fit in
744 * a cursor window, or 0 if unknown.
745 * @return The recommended start position, always less than or equal to
746 * the requested row.
747 * @hide
748 */
749 public static int cursorPickFillWindowStartPosition(
750 int cursorPosition, int cursorWindowCapacity) {
751 return Math.max(cursorPosition - cursorWindowCapacity / 3, 0);
752 }
753
754 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800755 * Query the table for the number of rows in the table.
756 * @param db the database the table is in
757 * @param table the name of the table to query
758 * @return the number of rows in the table
759 */
760 public static long queryNumEntries(SQLiteDatabase db, String table) {
Christian Mehlmauere7731f02010-06-16 22:56:07 +0200761 return queryNumEntries(db, table, null, null);
762 }
763
764 /**
765 * Query the table for the number of rows in the table.
766 * @param db the database the table is in
767 * @param table the name of the table to query
768 * @param selection A filter declaring which rows to return,
769 * formatted as an SQL WHERE clause (excluding the WHERE itself).
770 * Passing null will count all rows for the given table
771 * @return the number of rows in the table filtered by the selection
772 */
773 public static long queryNumEntries(SQLiteDatabase db, String table, String selection) {
774 return queryNumEntries(db, table, selection, null);
775 }
776
777 /**
778 * Query the table for the number of rows in the table.
779 * @param db the database the table is in
780 * @param table the name of the table to query
781 * @param selection A filter declaring which rows to return,
782 * formatted as an SQL WHERE clause (excluding the WHERE itself).
783 * Passing null will count all rows for the given table
784 * @param selectionArgs You may include ?s in selection,
785 * which will be replaced by the values from selectionArgs,
786 * in order that they appear in the selection.
787 * The values will be bound as Strings.
788 * @return the number of rows in the table filtered by the selection
789 */
790 public static long queryNumEntries(SQLiteDatabase db, String table, String selection,
791 String[] selectionArgs) {
792 String s = (!TextUtils.isEmpty(selection)) ? " where " + selection : "";
793 return longForQuery(db, "select count(*) from " + table + s,
794 selectionArgs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800795 }
796
797 /**
798 * Utility method to run the query on the db and return the value in the
799 * first column of the first row.
800 */
801 public static long longForQuery(SQLiteDatabase db, String query, String[] selectionArgs) {
802 SQLiteStatement prog = db.compileStatement(query);
803 try {
804 return longForQuery(prog, selectionArgs);
805 } finally {
806 prog.close();
807 }
808 }
809
810 /**
811 * Utility method to run the pre-compiled query and return the value in the
812 * first column of the first row.
813 */
814 public static long longForQuery(SQLiteStatement prog, String[] selectionArgs) {
Vasu Nori0732f792010-07-29 17:24:12 -0700815 prog.bindAllArgsAsStrings(selectionArgs);
816 return prog.simpleQueryForLong();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800817 }
818
819 /**
820 * Utility method to run the query on the db and return the value in the
821 * first column of the first row.
822 */
823 public static String stringForQuery(SQLiteDatabase db, String query, String[] selectionArgs) {
824 SQLiteStatement prog = db.compileStatement(query);
825 try {
826 return stringForQuery(prog, selectionArgs);
827 } finally {
828 prog.close();
829 }
830 }
831
832 /**
833 * Utility method to run the pre-compiled query and return the value in the
834 * first column of the first row.
835 */
836 public static String stringForQuery(SQLiteStatement prog, String[] selectionArgs) {
Vasu Nori0732f792010-07-29 17:24:12 -0700837 prog.bindAllArgsAsStrings(selectionArgs);
838 return prog.simpleQueryForString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800839 }
840
841 /**
Bjorn Bringerta006b4722010-04-14 14:43:26 +0100842 * Utility method to run the query on the db and return the blob value in the
843 * first column of the first row.
844 *
845 * @return A read-only file descriptor for a copy of the blob value.
846 */
847 public static ParcelFileDescriptor blobFileDescriptorForQuery(SQLiteDatabase db,
848 String query, String[] selectionArgs) {
849 SQLiteStatement prog = db.compileStatement(query);
850 try {
851 return blobFileDescriptorForQuery(prog, selectionArgs);
852 } finally {
853 prog.close();
854 }
855 }
856
857 /**
858 * Utility method to run the pre-compiled query and return the blob value in the
859 * first column of the first row.
860 *
861 * @return A read-only file descriptor for a copy of the blob value.
862 */
863 public static ParcelFileDescriptor blobFileDescriptorForQuery(SQLiteStatement prog,
864 String[] selectionArgs) {
865 prog.bindAllArgsAsStrings(selectionArgs);
866 return prog.simpleQueryForBlobFileDescriptor();
867 }
868
869 /**
Fred Quintana2ec6c562009-12-09 16:00:31 -0800870 * Reads a String out of a column in a Cursor and writes it to a ContentValues.
871 * Adds nothing to the ContentValues if the column isn't present or if its value is null.
872 *
873 * @param cursor The cursor to read from
874 * @param column The column to read
875 * @param values The {@link ContentValues} to put the value into
876 */
877 public static void cursorStringToContentValuesIfPresent(Cursor cursor, ContentValues values,
878 String column) {
Dmitri Plotnikov9a9ce602010-07-26 15:52:07 -0700879 final int index = cursor.getColumnIndex(column);
880 if (index != -1 && !cursor.isNull(index)) {
Fred Quintana2ec6c562009-12-09 16:00:31 -0800881 values.put(column, cursor.getString(index));
882 }
883 }
884
885 /**
886 * Reads a Long out of a column in a Cursor and writes it to a ContentValues.
887 * Adds nothing to the ContentValues if the column isn't present or if its value is null.
888 *
889 * @param cursor The cursor to read from
890 * @param column The column to read
891 * @param values The {@link ContentValues} to put the value into
892 */
893 public static void cursorLongToContentValuesIfPresent(Cursor cursor, ContentValues values,
894 String column) {
Dmitri Plotnikov9a9ce602010-07-26 15:52:07 -0700895 final int index = cursor.getColumnIndex(column);
896 if (index != -1 && !cursor.isNull(index)) {
Fred Quintana2ec6c562009-12-09 16:00:31 -0800897 values.put(column, cursor.getLong(index));
898 }
899 }
900
901 /**
902 * Reads a Short out of a column in a Cursor and writes it to a ContentValues.
903 * Adds nothing to the ContentValues if the column isn't present or if its value is null.
904 *
905 * @param cursor The cursor to read from
906 * @param column The column to read
907 * @param values The {@link ContentValues} to put the value into
908 */
909 public static void cursorShortToContentValuesIfPresent(Cursor cursor, ContentValues values,
910 String column) {
Dmitri Plotnikov9a9ce602010-07-26 15:52:07 -0700911 final int index = cursor.getColumnIndex(column);
912 if (index != -1 && !cursor.isNull(index)) {
Fred Quintana2ec6c562009-12-09 16:00:31 -0800913 values.put(column, cursor.getShort(index));
914 }
915 }
916
917 /**
918 * Reads a Integer out of a column in a Cursor and writes it to a ContentValues.
919 * Adds nothing to the ContentValues if the column isn't present or if its value is null.
920 *
921 * @param cursor The cursor to read from
922 * @param column The column to read
923 * @param values The {@link ContentValues} to put the value into
924 */
925 public static void cursorIntToContentValuesIfPresent(Cursor cursor, ContentValues values,
926 String column) {
Dmitri Plotnikov9a9ce602010-07-26 15:52:07 -0700927 final int index = cursor.getColumnIndex(column);
928 if (index != -1 && !cursor.isNull(index)) {
Fred Quintana2ec6c562009-12-09 16:00:31 -0800929 values.put(column, cursor.getInt(index));
930 }
931 }
932
933 /**
934 * Reads a Float out of a column in a Cursor and writes it to a ContentValues.
935 * Adds nothing to the ContentValues if the column isn't present or if its value is null.
936 *
937 * @param cursor The cursor to read from
938 * @param column The column to read
939 * @param values The {@link ContentValues} to put the value into
940 */
941 public static void cursorFloatToContentValuesIfPresent(Cursor cursor, ContentValues values,
942 String column) {
Dmitri Plotnikov9a9ce602010-07-26 15:52:07 -0700943 final int index = cursor.getColumnIndex(column);
944 if (index != -1 && !cursor.isNull(index)) {
Fred Quintana2ec6c562009-12-09 16:00:31 -0800945 values.put(column, cursor.getFloat(index));
946 }
947 }
948
949 /**
950 * Reads a Double out of a column in a Cursor and writes it to a ContentValues.
951 * Adds nothing to the ContentValues if the column isn't present or if its value is null.
952 *
953 * @param cursor The cursor to read from
954 * @param column The column to read
955 * @param values The {@link ContentValues} to put the value into
956 */
957 public static void cursorDoubleToContentValuesIfPresent(Cursor cursor, ContentValues values,
958 String column) {
Dmitri Plotnikov9a9ce602010-07-26 15:52:07 -0700959 final int index = cursor.getColumnIndex(column);
960 if (index != -1 && !cursor.isNull(index)) {
Fred Quintana2ec6c562009-12-09 16:00:31 -0800961 values.put(column, cursor.getDouble(index));
962 }
963 }
964
965 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800966 * This class allows users to do multiple inserts into a table but
967 * compile the SQL insert statement only once, which may increase
968 * performance.
969 */
970 public static class InsertHelper {
971 private final SQLiteDatabase mDb;
972 private final String mTableName;
973 private HashMap<String, Integer> mColumns;
974 private String mInsertSQL = null;
975 private SQLiteStatement mInsertStatement = null;
976 private SQLiteStatement mReplaceStatement = null;
977 private SQLiteStatement mPreparedStatement = null;
978
979 /**
980 * {@hide}
981 *
982 * These are the columns returned by sqlite's "PRAGMA
983 * table_info(...)" command that we depend on.
984 */
985 public static final int TABLE_INFO_PRAGMA_COLUMNNAME_INDEX = 1;
986 public static final int TABLE_INFO_PRAGMA_DEFAULT_INDEX = 4;
987
988 /**
989 * @param db the SQLiteDatabase to insert into
990 * @param tableName the name of the table to insert into
991 */
992 public InsertHelper(SQLiteDatabase db, String tableName) {
993 mDb = db;
994 mTableName = tableName;
995 }
996
997 private void buildSQL() throws SQLException {
998 StringBuilder sb = new StringBuilder(128);
999 sb.append("INSERT INTO ");
1000 sb.append(mTableName);
1001 sb.append(" (");
1002
1003 StringBuilder sbv = new StringBuilder(128);
1004 sbv.append("VALUES (");
1005
1006 int i = 1;
1007 Cursor cur = null;
1008 try {
1009 cur = mDb.rawQuery("PRAGMA table_info(" + mTableName + ")", null);
1010 mColumns = new HashMap<String, Integer>(cur.getCount());
1011 while (cur.moveToNext()) {
1012 String columnName = cur.getString(TABLE_INFO_PRAGMA_COLUMNNAME_INDEX);
1013 String defaultValue = cur.getString(TABLE_INFO_PRAGMA_DEFAULT_INDEX);
1014
1015 mColumns.put(columnName, i);
1016 sb.append("'");
1017 sb.append(columnName);
1018 sb.append("'");
1019
1020 if (defaultValue == null) {
1021 sbv.append("?");
1022 } else {
1023 sbv.append("COALESCE(?, ");
1024 sbv.append(defaultValue);
1025 sbv.append(")");
1026 }
1027
1028 sb.append(i == cur.getCount() ? ") " : ", ");
1029 sbv.append(i == cur.getCount() ? ");" : ", ");
1030 ++i;
1031 }
1032 } finally {
1033 if (cur != null) cur.close();
1034 }
1035
1036 sb.append(sbv);
1037
1038 mInsertSQL = sb.toString();
1039 if (LOCAL_LOGV) Log.v(TAG, "insert statement is " + mInsertSQL);
1040 }
1041
1042 private SQLiteStatement getStatement(boolean allowReplace) throws SQLException {
1043 if (allowReplace) {
1044 if (mReplaceStatement == null) {
1045 if (mInsertSQL == null) buildSQL();
1046 // chop "INSERT" off the front and prepend "INSERT OR REPLACE" instead.
1047 String replaceSQL = "INSERT OR REPLACE" + mInsertSQL.substring(6);
1048 mReplaceStatement = mDb.compileStatement(replaceSQL);
1049 }
1050 return mReplaceStatement;
1051 } else {
1052 if (mInsertStatement == null) {
1053 if (mInsertSQL == null) buildSQL();
1054 mInsertStatement = mDb.compileStatement(mInsertSQL);
1055 }
1056 return mInsertStatement;
1057 }
1058 }
1059
1060 /**
1061 * Performs an insert, adding a new row with the given values.
1062 *
1063 * @param values the set of values with which to populate the
1064 * new row
1065 * @param allowReplace if true, the statement does "INSERT OR
1066 * REPLACE" instead of "INSERT", silently deleting any
1067 * previously existing rows that would cause a conflict
1068 *
1069 * @return the row ID of the newly inserted row, or -1 if an
1070 * error occurred
1071 */
1072 private synchronized long insertInternal(ContentValues values, boolean allowReplace) {
1073 try {
1074 SQLiteStatement stmt = getStatement(allowReplace);
1075 stmt.clearBindings();
1076 if (LOCAL_LOGV) Log.v(TAG, "--- inserting in table " + mTableName);
1077 for (Map.Entry<String, Object> e: values.valueSet()) {
1078 final String key = e.getKey();
1079 int i = getColumnIndex(key);
1080 DatabaseUtils.bindObjectToProgram(stmt, i, e.getValue());
1081 if (LOCAL_LOGV) {
1082 Log.v(TAG, "binding " + e.getValue() + " to column " +
1083 i + " (" + key + ")");
1084 }
1085 }
1086 return stmt.executeInsert();
1087 } catch (SQLException e) {
1088 Log.e(TAG, "Error inserting " + values + " into table " + mTableName, e);
1089 return -1;
1090 }
1091 }
1092
1093 /**
1094 * Returns the index of the specified column. This is index is suitagble for use
1095 * in calls to bind().
1096 * @param key the column name
1097 * @return the index of the column
1098 */
1099 public int getColumnIndex(String key) {
1100 getStatement(false);
1101 final Integer index = mColumns.get(key);
1102 if (index == null) {
1103 throw new IllegalArgumentException("column '" + key + "' is invalid");
1104 }
1105 return index;
1106 }
1107
1108 /**
1109 * Bind the value to an index. A prepareForInsert() or prepareForReplace()
1110 * without a matching execute() must have already have been called.
1111 * @param index the index of the slot to which to bind
1112 * @param value the value to bind
1113 */
1114 public void bind(int index, double value) {
1115 mPreparedStatement.bindDouble(index, value);
1116 }
1117
1118 /**
1119 * Bind the value to an index. A prepareForInsert() or prepareForReplace()
1120 * without a matching execute() must have already have been called.
1121 * @param index the index of the slot to which to bind
1122 * @param value the value to bind
1123 */
1124 public void bind(int index, float value) {
1125 mPreparedStatement.bindDouble(index, value);
1126 }
1127
1128 /**
1129 * Bind the value to an index. A prepareForInsert() or prepareForReplace()
1130 * without a matching execute() must have already have been called.
1131 * @param index the index of the slot to which to bind
1132 * @param value the value to bind
1133 */
1134 public void bind(int index, long value) {
1135 mPreparedStatement.bindLong(index, value);
1136 }
1137
1138 /**
1139 * Bind the value to an index. A prepareForInsert() or prepareForReplace()
1140 * without a matching execute() must have already have been called.
1141 * @param index the index of the slot to which to bind
1142 * @param value the value to bind
1143 */
1144 public void bind(int index, int value) {
1145 mPreparedStatement.bindLong(index, value);
1146 }
1147
1148 /**
1149 * Bind the value to an index. A prepareForInsert() or prepareForReplace()
1150 * without a matching execute() must have already have been called.
1151 * @param index the index of the slot to which to bind
1152 * @param value the value to bind
1153 */
1154 public void bind(int index, boolean value) {
1155 mPreparedStatement.bindLong(index, value ? 1 : 0);
1156 }
1157
1158 /**
1159 * Bind null to an index. A prepareForInsert() or prepareForReplace()
1160 * without a matching execute() must have already have been called.
1161 * @param index the index of the slot to which to bind
1162 */
1163 public void bindNull(int index) {
1164 mPreparedStatement.bindNull(index);
1165 }
1166
1167 /**
1168 * Bind the value to an index. A prepareForInsert() or prepareForReplace()
1169 * without a matching execute() must have already have been called.
1170 * @param index the index of the slot to which to bind
1171 * @param value the value to bind
1172 */
1173 public void bind(int index, byte[] value) {
1174 if (value == null) {
1175 mPreparedStatement.bindNull(index);
1176 } else {
1177 mPreparedStatement.bindBlob(index, value);
1178 }
1179 }
1180
1181 /**
1182 * Bind the value to an index. A prepareForInsert() or prepareForReplace()
1183 * without a matching execute() must have already have been called.
1184 * @param index the index of the slot to which to bind
1185 * @param value the value to bind
1186 */
1187 public void bind(int index, String value) {
1188 if (value == null) {
1189 mPreparedStatement.bindNull(index);
1190 } else {
1191 mPreparedStatement.bindString(index, value);
1192 }
1193 }
1194
1195 /**
1196 * Performs an insert, adding a new row with the given values.
1197 * If the table contains conflicting rows, an error is
1198 * returned.
1199 *
1200 * @param values the set of values with which to populate the
1201 * new row
1202 *
1203 * @return the row ID of the newly inserted row, or -1 if an
1204 * error occurred
1205 */
1206 public long insert(ContentValues values) {
1207 return insertInternal(values, false);
1208 }
1209
1210 /**
1211 * Execute the previously prepared insert or replace using the bound values
1212 * since the last call to prepareForInsert or prepareForReplace.
1213 *
1214 * <p>Note that calling bind() and then execute() is not thread-safe. The only thread-safe
1215 * way to use this class is to call insert() or replace().
1216 *
1217 * @return the row ID of the newly inserted row, or -1 if an
1218 * error occurred
1219 */
1220 public long execute() {
1221 if (mPreparedStatement == null) {
1222 throw new IllegalStateException("you must prepare this inserter before calling "
1223 + "execute");
1224 }
1225 try {
1226 if (LOCAL_LOGV) Log.v(TAG, "--- doing insert or replace in table " + mTableName);
1227 return mPreparedStatement.executeInsert();
1228 } catch (SQLException e) {
1229 Log.e(TAG, "Error executing InsertHelper with table " + mTableName, e);
1230 return -1;
1231 } finally {
1232 // you can only call this once per prepare
1233 mPreparedStatement = null;
1234 }
1235 }
1236
1237 /**
1238 * Prepare the InsertHelper for an insert. The pattern for this is:
1239 * <ul>
1240 * <li>prepareForInsert()
1241 * <li>bind(index, value);
1242 * <li>bind(index, value);
1243 * <li>...
1244 * <li>bind(index, value);
1245 * <li>execute();
1246 * </ul>
1247 */
1248 public void prepareForInsert() {
1249 mPreparedStatement = getStatement(false);
1250 mPreparedStatement.clearBindings();
1251 }
1252
1253 /**
1254 * Prepare the InsertHelper for a replace. The pattern for this is:
1255 * <ul>
1256 * <li>prepareForReplace()
1257 * <li>bind(index, value);
1258 * <li>bind(index, value);
1259 * <li>...
1260 * <li>bind(index, value);
1261 * <li>execute();
1262 * </ul>
1263 */
1264 public void prepareForReplace() {
1265 mPreparedStatement = getStatement(true);
1266 mPreparedStatement.clearBindings();
1267 }
1268
1269 /**
1270 * Performs an insert, adding a new row with the given values.
1271 * If the table contains conflicting rows, they are deleted
1272 * and replaced with the new row.
1273 *
1274 * @param values the set of values with which to populate the
1275 * new row
1276 *
1277 * @return the row ID of the newly inserted row, or -1 if an
1278 * error occurred
1279 */
1280 public long replace(ContentValues values) {
1281 return insertInternal(values, true);
1282 }
1283
1284 /**
1285 * Close this object and release any resources associated with
1286 * it. The behavior of calling <code>insert()</code> after
1287 * calling this method is undefined.
1288 */
1289 public void close() {
1290 if (mInsertStatement != null) {
1291 mInsertStatement.close();
1292 mInsertStatement = null;
1293 }
1294 if (mReplaceStatement != null) {
1295 mReplaceStatement.close();
1296 mReplaceStatement = null;
1297 }
1298 mInsertSQL = null;
1299 mColumns = null;
1300 }
1301 }
1302
1303 /**
1304 * Creates a db and populates it with the sql statements in sqlStatements.
1305 *
1306 * @param context the context to use to create the db
1307 * @param dbName the name of the db to create
1308 * @param dbVersion the version to set on the db
1309 * @param sqlStatements the statements to use to populate the db. This should be a single string
1310 * of the form returned by sqlite3's <tt>.dump</tt> command (statements separated by
1311 * semicolons)
1312 */
1313 static public void createDbFromSqlStatements(
1314 Context context, String dbName, int dbVersion, String sqlStatements) {
1315 SQLiteDatabase db = context.openOrCreateDatabase(dbName, 0, null);
1316 // TODO: this is not quite safe since it assumes that all semicolons at the end of a line
1317 // terminate statements. It is possible that a text field contains ;\n. We will have to fix
1318 // this if that turns out to be a problem.
1319 String[] statements = TextUtils.split(sqlStatements, ";\n");
1320 for (String statement : statements) {
1321 if (TextUtils.isEmpty(statement)) continue;
1322 db.execSQL(statement);
1323 }
1324 db.setVersion(dbVersion);
1325 db.close();
1326 }
Vasu Norice38b982010-07-22 13:57:13 -07001327
1328 /**
1329 * Returns one of the following which represent the type of the given SQL statement.
1330 * <ol>
1331 * <li>{@link #STATEMENT_SELECT}</li>
1332 * <li>{@link #STATEMENT_UPDATE}</li>
1333 * <li>{@link #STATEMENT_ATTACH}</li>
1334 * <li>{@link #STATEMENT_BEGIN}</li>
1335 * <li>{@link #STATEMENT_COMMIT}</li>
1336 * <li>{@link #STATEMENT_ABORT}</li>
1337 * <li>{@link #STATEMENT_OTHER}</li>
1338 * </ol>
1339 * @param sql the SQL statement whose type is returned by this method
1340 * @return one of the values listed above
1341 */
1342 public static int getSqlStatementType(String sql) {
1343 sql = sql.trim();
1344 if (sql.length() < 3) {
1345 return STATEMENT_OTHER;
1346 }
1347 String prefixSql = sql.substring(0, 3).toUpperCase();
1348 if (prefixSql.equals("SEL")) {
1349 return STATEMENT_SELECT;
1350 } else if (prefixSql.equals("INS") ||
1351 prefixSql.equals("UPD") ||
1352 prefixSql.equals("REP") ||
1353 prefixSql.equals("DEL")) {
1354 return STATEMENT_UPDATE;
1355 } else if (prefixSql.equals("ATT")) {
1356 return STATEMENT_ATTACH;
1357 } else if (prefixSql.equals("COM")) {
1358 return STATEMENT_COMMIT;
1359 } else if (prefixSql.equals("END")) {
1360 return STATEMENT_COMMIT;
1361 } else if (prefixSql.equals("ROL")) {
1362 return STATEMENT_ABORT;
1363 } else if (prefixSql.equals("BEG")) {
1364 return STATEMENT_BEGIN;
Vasu Nori4e874ed2010-09-15 18:40:49 -07001365 } else if (prefixSql.equals("PRA")) {
1366 return STATEMENT_PRAGMA;
1367 } else if (prefixSql.equals("CRE") || prefixSql.equals("DRO") ||
1368 prefixSql.equals("ALT")) {
1369 return STATEMENT_DDL;
1370 } else if (prefixSql.equals("ANA") || prefixSql.equals("DET")) {
1371 return STATEMENT_UNPREPARED;
Vasu Norice38b982010-07-22 13:57:13 -07001372 }
1373 return STATEMENT_OTHER;
1374 }
Jeff Hamiltonf0cfe342010-08-09 16:54:05 -05001375
1376 /**
1377 * Appends one set of selection args to another. This is useful when adding a selection
1378 * argument to a user provided set.
1379 */
1380 public static String[] appendSelectionArgs(String[] originalValues, String[] newValues) {
1381 if (originalValues == null || originalValues.length == 0) {
1382 return newValues;
1383 }
1384 String[] result = new String[originalValues.length + newValues.length ];
1385 System.arraycopy(originalValues, 0, result, 0, originalValues.length);
1386 System.arraycopy(newValues, 0, result, originalValues.length, newValues.length);
1387 return result;
1388 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001389}