gnunet-android

GNUnet for Android
Log | Files | Refs | README

DatabaseUtils.java (55109B)


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