Add helper function to insert with retry.

This breaks the code that tries to insert into a database with a
random id into a helper function, since it's now shared between two
functions.

Test: keystore2_test
Change-Id: Ib03b6082246120e6b3ff2a45b5bf8d324c40f7a7
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 75f2027..d53bd4b 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -268,27 +268,14 @@
                     .context(format!("Domain {:?} must be either App or SELinux.", domain));
             }
         }
-        // Loop until we get a unique id.
-        loop {
-            let newid: i64 = random();
-            let ret = self.conn.execute(
+        Self::insert_with_retry(|id| {
+            self.conn.execute(
                 "INSERT into persistent.keyentry (id, creation_date, domain, namespace, alias)
                      VALUES(?, datetime('now'), ?, ?, NULL);",
-                params![newid, domain as i64, namespace],
-            );
-            match ret {
-                // If the id already existed, try again.
-                Err(rusqlite::Error::SqliteFailure(
-                    libsqlite3_sys::Error {
-                        code: libsqlite3_sys::ErrorCode::ConstraintViolation,
-                        extended_code: libsqlite3_sys::SQLITE_CONSTRAINT_UNIQUE,
-                    },
-                    _,
-                )) => (),
-                Err(e) => return Err(e).context("Failed to create key entry."),
-                _ => return Ok(newid),
-            }
-        }
+                params![id, domain as i64, namespace],
+            )
+        })
+        .context("In create_key_entry")
     }
 
     /// Inserts a new blob and associates it with the given key id. Each blob
@@ -611,26 +598,14 @@
             .context("In grant: Failed to update existing grant.")?;
             grant_id
         } else {
-            loop {
-                let newid: i64 = random();
-                let ret = tx.execute(
+            Self::insert_with_retry(|id| {
+                tx.execute(
                     "INSERT INTO perboot.grant (id, grantee, keyentryid, access_vector)
                         VALUES (?, ?, ?, ?);",
-                    params![newid, grantee_uid, key_id, i32::from(access_vector)],
-                );
-                match ret {
-                    // If the id already existed, try again.
-                    Err(rusqlite::Error::SqliteFailure(
-                        libsqlite3_sys::Error {
-                            code: libsqlite3_sys::ErrorCode::ConstraintViolation,
-                            extended_code: libsqlite3_sys::SQLITE_CONSTRAINT_UNIQUE,
-                        },
-                        _,
-                    )) => (),
-                    Err(e) => return Err(e).context("Failed to insert grant."),
-                    Ok(_) => break newid,
-                }
-            }
+                    params![id, grantee_uid, key_id, i32::from(access_vector)],
+                )
+            })
+            .context("In grant")?
         };
         tx.commit().context("In grant: failed to commit transaction.")?;
 
@@ -672,6 +647,29 @@
         Ok(())
     }
 
+    // Generates a random id and passes it to the given function, which will
+    // try to insert it into a database.  If that insertion fails, retry;
+    // otherwise return the id.
+    fn insert_with_retry(inserter: impl Fn(i64) -> rusqlite::Result<usize>) -> Result<i64> {
+        loop {
+            let newid: i64 = random();
+            match inserter(newid) {
+                // If the id already existed, try again.
+                Err(rusqlite::Error::SqliteFailure(
+                    libsqlite3_sys::Error {
+                        code: libsqlite3_sys::ErrorCode::ConstraintViolation,
+                        extended_code: libsqlite3_sys::SQLITE_CONSTRAINT_UNIQUE,
+                    },
+                    _,
+                )) => (),
+                Err(e) => {
+                    return Err(e).context("In insert_with_retry: failed to insert into database.")
+                }
+                _ => return Ok(newid),
+            }
+        }
+    }
+
     // Takes Rows as returned by a query call on prepared statement.
     // Extracts exactly one row with the `row_extractor` and fails if more
     // rows are available.