aboutsummaryrefslogtreecommitdiff
path: root/src/com
diff options
context:
space:
mode:
authorTa-wei Yen <twyen@google.com>2016-01-26 18:24:34 -0800
committerTa-wei Yen <twyen@google.com>2016-01-26 18:24:34 -0800
commit155ff44c5809da846b841011923b7e81dede303d (patch)
treefc8656e38d62478c4db1d8b70a76c962e1110db3 /src/com
parent7d8c2d4c22024478a36cc57b9288ca5e7b3fb7e6 (diff)
downloadContactsProvider-155ff44c5809da846b841011923b7e81dede303d.tar.gz
Implement UPSERT for VoicemailStatusTable#insert
When putting data into the voicemail status table we used to have 2 separate database operations: 1) check if the index exists 2) insert into the database if not exist, or update if exists. This is a potential race condition if 2 threads try to write at the same time, pointed out in ag/838999 This CL solve it by changing the semantic of INSERT to "UPSERT", "Update or insert", which is a single database operation of "insert into the database if not exist, or update if exists" SQLite does not directly support UPSERT, and doing so in a single query is complicated. Instead insert, query, update, and delete in VoicemailStatusTable is changed to be synchronized with a singleton, so there can only be one thread doing a set of operations on the table at once. Large traffic on VoicemailStatusTable is unlikely so the synchronization cost is acceptable. Bug:26451384 Change-Id: I6c03bd2720cbb05738cb3059fd19d65156244e20
Diffstat (limited to 'src/com')
-rw-r--r--src/com/android/providers/contacts/VoicemailStatusTable.java80
1 files changed, 52 insertions, 28 deletions
diff --git a/src/com/android/providers/contacts/VoicemailStatusTable.java b/src/com/android/providers/contacts/VoicemailStatusTable.java
index b34c4819..c07d4b16 100644
--- a/src/com/android/providers/contacts/VoicemailStatusTable.java
+++ b/src/com/android/providers/contacts/VoicemailStatusTable.java
@@ -33,6 +33,9 @@ import com.android.providers.contacts.VoicemailContentProvider.UriData;
/**
* Implementation of {@link VoicemailTable.Delegate} for the voicemail status table.
+ *
+ * Public methods of this class are thread-safe as it is used in a content provider, which should
+ * be thread-safe.
*/
public class VoicemailStatusTable implements VoicemailTable.Delegate {
@@ -48,6 +51,8 @@ public class VoicemailStatusTable implements VoicemailTable.Delegate {
.add(Status.QUOTA_TOTAL)
.build();
+ private static final Object DATABASE_LOCK = new Object();
+
private final String mTableName;
private final Context mContext;
private final CallLogDatabaseHelper mDbHelper;
@@ -63,50 +68,69 @@ public class VoicemailStatusTable implements VoicemailTable.Delegate {
@Override
public Uri insert(UriData uriData, ContentValues values) {
- SQLiteDatabase db = mDbHelper.getWritableDatabase();
- ContentValues copiedValues = new ContentValues(values);
- mDelegateHelper.checkAndAddSourcePackageIntoValues(uriData, copiedValues);
- long rowId = getDatabaseModifier(db).insert(mTableName, null, copiedValues);
- if (rowId > 0) {
- Uri newUri = ContentUris.withAppendedId(uriData.getUri(), rowId);
- return newUri;
- } else {
- return null;
+ synchronized (DATABASE_LOCK) {
+ SQLiteDatabase db = mDbHelper.getWritableDatabase();
+ // Try to update before insert.
+ String combinedClause = uriData.getWhereClause();
+ int rowsChanged = getDatabaseModifier(db)
+ .update(mTableName, values, combinedClause, null);
+ if (rowsChanged != 0) {
+ final String[] selection = new String[] {Status._ID};
+ Cursor c = db.query(mTableName, selection, combinedClause, null, null, null, null);
+ c.moveToFirst();
+ int rowId = c.getInt(0);
+ return ContentUris.withAppendedId(uriData.getUri(), rowId);
+ }
+ ContentValues copiedValues = new ContentValues(values);
+ mDelegateHelper.checkAndAddSourcePackageIntoValues(uriData, copiedValues);
+ long rowId = getDatabaseModifier(db).insert(mTableName, null, copiedValues);
+ if (rowId > 0) {
+ return ContentUris.withAppendedId(uriData.getUri(), rowId);
+ } else {
+ return null;
+ }
}
}
@Override
public int delete(UriData uriData, String selection, String[] selectionArgs) {
- SQLiteDatabase db = mDbHelper.getWritableDatabase();
- String combinedClause = concatenateClauses(selection, uriData.getWhereClause());
- return getDatabaseModifier(db).delete(mTableName, combinedClause,
- selectionArgs);
+ synchronized (DATABASE_LOCK) {
+ SQLiteDatabase db = mDbHelper.getWritableDatabase();
+ String combinedClause = concatenateClauses(selection, uriData.getWhereClause());
+ return getDatabaseModifier(db).delete(mTableName, combinedClause,
+ selectionArgs);
+ }
}
@Override
public Cursor query(UriData uriData, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
- SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- qb.setTables(mTableName);
- qb.setProjectionMap(sStatusProjectionMap);
- qb.setStrict(true);
-
- String combinedClause = concatenateClauses(selection, uriData.getWhereClause());
- SQLiteDatabase db = mDbHelper.getReadableDatabase();
- Cursor c = qb.query(db, projection, combinedClause, selectionArgs, null, null, sortOrder);
- if (c != null) {
- c.setNotificationUri(mContext.getContentResolver(), Status.CONTENT_URI);
+ synchronized (DATABASE_LOCK) {
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ qb.setTables(mTableName);
+ qb.setProjectionMap(sStatusProjectionMap);
+ qb.setStrict(true);
+
+ String combinedClause = concatenateClauses(selection, uriData.getWhereClause());
+ SQLiteDatabase db = mDbHelper.getReadableDatabase();
+ Cursor c = qb
+ .query(db, projection, combinedClause, selectionArgs, null, null, sortOrder);
+ if (c != null) {
+ c.setNotificationUri(mContext.getContentResolver(), Status.CONTENT_URI);
+ }
+ return c;
}
- return c;
}
@Override
public int update(UriData uriData, ContentValues values, String selection,
String[] selectionArgs) {
- SQLiteDatabase db = mDbHelper.getWritableDatabase();
- String combinedClause = concatenateClauses(selection, uriData.getWhereClause());
- return getDatabaseModifier(db).update(mTableName, values, combinedClause,
- selectionArgs);
+ synchronized (DATABASE_LOCK) {
+ SQLiteDatabase db = mDbHelper.getWritableDatabase();
+ String combinedClause = concatenateClauses(selection, uriData.getWhereClause());
+ return getDatabaseModifier(db).update(mTableName, values, combinedClause,
+ selectionArgs);
+ }
}
@Override