<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[209144] trunk/Source/WebCore</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/209144">209144</a></dd>
<dt>Author</dt> <dd>beidson@apple.com</dd>
<dt>Date</dt> <dd>2016-11-30 11:59:01 -0800 (Wed, 30 Nov 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>IndexedDB 2.0: Cache and reuse SQLiteStatements in the SQLite backend.
https://bugs.webkit.org/show_bug.cgi?id=164974

Reviewed by Alex Christensen.

No new tests (No behavior change other than being faster).

Instead of building new SQLiteStatements from scratch each time they're needed and then
finalizing them after they're used for a single operation, we cache them.

For tests that do puts and/or gets in tight loops, this shows up as a 5%-20% speedup on profiles.

* Modules/indexeddb/server/SQLiteIDBBackingStore.cpp:
(WebCore::IDBServer::SQLiteIDBBackingStore::~SQLiteIDBBackingStore):
(WebCore::IDBServer::SQLiteIDBBackingStore::createAndPopulateInitialDatabaseInfo):
(WebCore::IDBServer::SQLiteIDBBackingStore::getOrEstablishDatabaseInfo):
(WebCore::IDBServer::SQLiteIDBBackingStore::createObjectStore):
(WebCore::IDBServer::SQLiteIDBBackingStore::deleteObjectStore):
(WebCore::IDBServer::SQLiteIDBBackingStore::renameObjectStore):
(WebCore::IDBServer::SQLiteIDBBackingStore::clearObjectStore):
(WebCore::IDBServer::SQLiteIDBBackingStore::createIndex):
(WebCore::IDBServer::SQLiteIDBBackingStore::uncheckedHasIndexRecord):
(WebCore::IDBServer::SQLiteIDBBackingStore::uncheckedPutIndexRecord):
(WebCore::IDBServer::SQLiteIDBBackingStore::deleteIndex):
(WebCore::IDBServer::SQLiteIDBBackingStore::renameIndex):
(WebCore::IDBServer::SQLiteIDBBackingStore::keyExistsInObjectStore):
(WebCore::IDBServer::SQLiteIDBBackingStore::deleteUnusedBlobFileRecords):
(WebCore::IDBServer::SQLiteIDBBackingStore::deleteRecord):
(WebCore::IDBServer::SQLiteIDBBackingStore::updateAllIndexesForAddRecord):
(WebCore::IDBServer::SQLiteIDBBackingStore::addRecord):
(WebCore::IDBServer::SQLiteIDBBackingStore::getBlobRecordsForObjectStoreRecord):
(WebCore::IDBServer::SQLiteIDBBackingStore::getRecord):
(WebCore::IDBServer::SQLiteIDBBackingStore::cachedStatementForGetAllObjectStoreRecords):
(WebCore::IDBServer::SQLiteIDBBackingStore::getAllObjectStoreRecords):
(WebCore::IDBServer::SQLiteIDBBackingStore::uncheckedGetKeyGeneratorValue):
(WebCore::IDBServer::SQLiteIDBBackingStore::uncheckedSetKeyGeneratorValue):
(WebCore::IDBServer::SQLiteIDBBackingStore::deleteBackingStore):
(WebCore::IDBServer::SQLiteIDBBackingStore::cachedStatement):
(WebCore::IDBServer::SQLiteIDBBackingStore::closeSQLiteDB):
(WebCore::IDBServer::queryForGetAllObjectStoreRecords): Deleted.
* Modules/indexeddb/server/SQLiteIDBBackingStore.h:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreModulesindexeddbserverSQLiteIDBBackingStorecpp">trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp</a></li>
<li><a href="#trunkSourceWebCoreModulesindexeddbserverSQLiteIDBBackingStoreh">trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (209143 => 209144)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-11-30 19:48:04 UTC (rev 209143)
+++ trunk/Source/WebCore/ChangeLog        2016-11-30 19:59:01 UTC (rev 209144)
</span><span class="lines">@@ -1,3 +1,47 @@
</span><ins>+2016-11-30  Brady Eidson  &lt;beidson@apple.com&gt;
+
+        IndexedDB 2.0: Cache and reuse SQLiteStatements in the SQLite backend.
+        https://bugs.webkit.org/show_bug.cgi?id=164974
+
+        Reviewed by Alex Christensen.
+
+        No new tests (No behavior change other than being faster).
+
+        Instead of building new SQLiteStatements from scratch each time they're needed and then
+        finalizing them after they're used for a single operation, we cache them.
+
+        For tests that do puts and/or gets in tight loops, this shows up as a 5%-20% speedup on profiles.
+
+        * Modules/indexeddb/server/SQLiteIDBBackingStore.cpp:
+        (WebCore::IDBServer::SQLiteIDBBackingStore::~SQLiteIDBBackingStore):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::createAndPopulateInitialDatabaseInfo):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::getOrEstablishDatabaseInfo):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::createObjectStore):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::deleteObjectStore):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::renameObjectStore):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::clearObjectStore):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::createIndex):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::uncheckedHasIndexRecord):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::uncheckedPutIndexRecord):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::deleteIndex):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::renameIndex):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::keyExistsInObjectStore):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::deleteUnusedBlobFileRecords):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::deleteRecord):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::updateAllIndexesForAddRecord):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::addRecord):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::getBlobRecordsForObjectStoreRecord):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::getRecord):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::cachedStatementForGetAllObjectStoreRecords):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::getAllObjectStoreRecords):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::uncheckedGetKeyGeneratorValue):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::uncheckedSetKeyGeneratorValue):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::deleteBackingStore):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::cachedStatement):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::closeSQLiteDB):
+        (WebCore::IDBServer::queryForGetAllObjectStoreRecords): Deleted.
+        * Modules/indexeddb/server/SQLiteIDBBackingStore.h:
+
</ins><span class="cx"> 2016-11-30  Dave Hyatt  &lt;hyatt@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [CSS Parser] Make sure -webkit-background-size coalesces identical values.
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesindexeddbserverSQLiteIDBBackingStorecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp (209143 => 209144)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp        2016-11-30 19:48:04 UTC (rev 209143)
+++ trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp        2016-11-30 19:59:01 UTC (rev 209144)
</span><span class="lines">@@ -214,7 +214,7 @@
</span><span class="cx"> SQLiteIDBBackingStore::~SQLiteIDBBackingStore()
</span><span class="cx"> {
</span><span class="cx">     if (m_sqliteDB)
</span><del>-        m_sqliteDB-&gt;close();
</del><ins>+        closeSQLiteDB();
</ins><span class="cx"> 
</span><span class="cx">     if (m_vm) {
</span><span class="cx">         JSLockHolder locker(m_vm.get());
</span><span class="lines">@@ -490,25 +490,25 @@
</span><span class="cx"> 
</span><span class="cx">     if (!m_sqliteDB-&gt;executeCommand(&quot;CREATE TABLE IDBDatabaseInfo (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE, value TEXT NOT NULL ON CONFLICT FAIL);&quot;)) {
</span><span class="cx">         LOG_ERROR(&quot;Could not create IDBDatabaseInfo table in database (%i) - %s&quot;, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><del>-        m_sqliteDB = nullptr;
</del><ins>+        closeSQLiteDB();
</ins><span class="cx">         return nullptr;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (!m_sqliteDB-&gt;executeCommand(&quot;CREATE TABLE ObjectStoreInfo (id INTEGER PRIMARY KEY NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT FAIL, name TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT FAIL, keyPath BLOB NOT NULL ON CONFLICT FAIL, autoInc INTEGER NOT NULL ON CONFLICT FAIL, maxIndexID INTEGER NOT NULL ON CONFLICT FAIL);&quot;)) {
</span><span class="cx">         LOG_ERROR(&quot;Could not create ObjectStoreInfo table in database (%i) - %s&quot;, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><del>-        m_sqliteDB = nullptr;
</del><ins>+        closeSQLiteDB();
</ins><span class="cx">         return nullptr;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (!m_sqliteDB-&gt;executeCommand(&quot;CREATE TABLE IndexInfo (id INTEGER NOT NULL ON CONFLICT FAIL, name TEXT NOT NULL ON CONFLICT FAIL, objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, keyPath BLOB NOT NULL ON CONFLICT FAIL, isUnique INTEGER NOT NULL ON CONFLICT FAIL, multiEntry INTEGER NOT NULL ON CONFLICT FAIL);&quot;)) {
</span><span class="cx">         LOG_ERROR(&quot;Could not create IndexInfo table in database (%i) - %s&quot;, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><del>-        m_sqliteDB = nullptr;
</del><ins>+        closeSQLiteDB();
</ins><span class="cx">         return nullptr;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (!m_sqliteDB-&gt;executeCommand(&quot;CREATE TABLE KeyGenerators (objectStoreID INTEGER NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE, currentKey INTEGER NOT NULL ON CONFLICT FAIL);&quot;)) {
</span><span class="cx">         LOG_ERROR(&quot;Could not create KeyGenerators table in database (%i) - %s&quot;, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><del>-        m_sqliteDB = nullptr;
</del><ins>+        closeSQLiteDB();
</ins><span class="cx">         return nullptr;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -518,7 +518,7 @@
</span><span class="cx">             || sql.bindInt(1, currentMetadataVersion) != SQLITE_OK
</span><span class="cx">             || sql.step() != SQLITE_DONE) {
</span><span class="cx">             LOG_ERROR(&quot;Could not insert database metadata version into IDBDatabaseInfo table (%i) - %s&quot;, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><del>-            m_sqliteDB = nullptr;
</del><ins>+            closeSQLiteDB();
</ins><span class="cx">             return nullptr;
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="lines">@@ -528,7 +528,7 @@
</span><span class="cx">             || sql.bindText(1, m_identifier.databaseName()) != SQLITE_OK
</span><span class="cx">             || sql.step() != SQLITE_DONE) {
</span><span class="cx">             LOG_ERROR(&quot;Could not insert database name into IDBDatabaseInfo table (%i) - %s&quot;, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><del>-            m_sqliteDB = nullptr;
</del><ins>+            closeSQLiteDB();
</ins><span class="cx">             return nullptr;
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="lines">@@ -540,7 +540,7 @@
</span><span class="cx">             || sql.bindText(1, String::number(0)) != SQLITE_OK
</span><span class="cx">             || sql.step() != SQLITE_DONE) {
</span><span class="cx">             LOG_ERROR(&quot;Could not insert default version into IDBDatabaseInfo table (%i) - %s&quot;, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><del>-            m_sqliteDB = nullptr;
</del><ins>+            closeSQLiteDB();
</ins><span class="cx">             return nullptr;
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="lines">@@ -547,7 +547,7 @@
</span><span class="cx"> 
</span><span class="cx">     if (!m_sqliteDB-&gt;executeCommand(ASCIILiteral(&quot;INSERT INTO IDBDatabaseInfo VALUES ('MaxObjectStoreID', 1);&quot;))) {
</span><span class="cx">         LOG_ERROR(&quot;Could not insert default version into IDBDatabaseInfo table (%i) - %s&quot;, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><del>-        m_sqliteDB = nullptr;
</del><ins>+        closeSQLiteDB();
</ins><span class="cx">         return nullptr;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -721,7 +721,7 @@
</span><span class="cx">     m_sqliteDB = std::make_unique&lt;SQLiteDatabase&gt;();
</span><span class="cx">     if (!m_sqliteDB-&gt;open(dbFilename)) {
</span><span class="cx">         LOG_ERROR(&quot;Failed to open SQLite database at path '%s'&quot;, dbFilename.utf8().data());
</span><del>-        m_sqliteDB = nullptr;
</del><ins>+        closeSQLiteDB();
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (!m_sqliteDB)
</span><span class="lines">@@ -733,19 +733,19 @@
</span><span class="cx"> 
</span><span class="cx">     if (!ensureValidRecordsTable()) {
</span><span class="cx">         LOG_ERROR(&quot;Error creating or migrating Records table in database&quot;);
</span><del>-        m_sqliteDB = nullptr;
</del><ins>+        closeSQLiteDB();
</ins><span class="cx">         return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Error creating or migrating Records table in database&quot;) };
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (!ensureValidIndexRecordsTable()) {
</span><span class="cx">         LOG_ERROR(&quot;Error creating or migrating Index Records table in database&quot;);
</span><del>-        m_sqliteDB = nullptr;
</del><ins>+        closeSQLiteDB();
</ins><span class="cx">         return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Error creating or migrating Index Records table in database&quot;) };
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (!ensureValidBlobTables()) {
</span><span class="cx">         LOG_ERROR(&quot;Error creating or confirming Blob Records tables in database&quot;);
</span><del>-        m_sqliteDB = nullptr;
</del><ins>+        closeSQLiteDB();
</ins><span class="cx">         return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Error creating or confirming Blob Records tables in database&quot;) };
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -755,7 +755,7 @@
</span><span class="cx"> 
</span><span class="cx">     if (!databaseInfo) {
</span><span class="cx">         LOG_ERROR(&quot;Unable to establish IDB database at path '%s'&quot;, dbFilename.utf8().data());
</span><del>-        m_sqliteDB = nullptr;
</del><ins>+        closeSQLiteDB();
</ins><span class="cx">         return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Unable to establish IDB database file&quot;) };
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -862,14 +862,14 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;INSERT INTO ObjectStoreInfo VALUES (?, ?, ?, ?, ?);&quot;));
-        if (sql.prepare() != SQLITE_OK
-            || sql.bindInt64(1, info.identifier()) != SQLITE_OK
-            || sql.bindText(2, info.name()) != SQLITE_OK
-            || sql.bindBlob(3, keyPathBlob-&gt;data(), keyPathBlob-&gt;size()) != SQLITE_OK
-            || sql.bindInt(4, info.autoIncrement()) != SQLITE_OK
-            || sql.bindInt64(5, info.maxIndexID()) != SQLITE_OK
-            || sql.step() != SQLITE_DONE) {
</del><ins>+        auto* sql = cachedStatement(SQL::CreateObjectStoreInfo, ASCIILiteral(&quot;INSERT INTO ObjectStoreInfo VALUES (?, ?, ?, ?, ?);&quot;));
+        if (!sql
+            || sql-&gt;bindInt64(1, info.identifier()) != SQLITE_OK
+            || sql-&gt;bindText(2, info.name()) != SQLITE_OK
+            || sql-&gt;bindBlob(3, keyPathBlob-&gt;data(), keyPathBlob-&gt;size()) != SQLITE_OK
+            || sql-&gt;bindInt(4, info.autoIncrement()) != SQLITE_OK
+            || sql-&gt;bindInt64(5, info.maxIndexID()) != SQLITE_OK
+            || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">             LOG_ERROR(&quot;Could not add object store '%s' to ObjectStoreInfo table (%i) - %s&quot;, info.name().utf8().data(), m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Could not create object store&quot;) };
</span><span class="cx">         }
</span><span class="lines">@@ -876,10 +876,10 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;INSERT INTO KeyGenerators VALUES (?, 0);&quot;));
-        if (sql.prepare() != SQLITE_OK
-            || sql.bindInt64(1, info.identifier()) != SQLITE_OK
-            || sql.step() != SQLITE_DONE) {
</del><ins>+        auto* sql = cachedStatement(SQL::CreateObjectStoreKeyGenerator, ASCIILiteral(&quot;INSERT INTO KeyGenerators VALUES (?, 0);&quot;));
+        if (!sql
+            || sql-&gt;bindInt64(1, info.identifier()) != SQLITE_OK
+            || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">             LOG_ERROR(&quot;Could not seed initial key generator value for ObjectStoreInfo table (%i) - %s&quot;, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Could not seed initial key generator value for object store&quot;) };
</span><span class="cx">         }
</span><span class="lines">@@ -909,10 +909,10 @@
</span><span class="cx"> 
</span><span class="cx">     // Delete the ObjectStore record
</span><span class="cx">     {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;DELETE FROM ObjectStoreInfo WHERE id = ?;&quot;));
-        if (sql.prepare() != SQLITE_OK
-            || sql.bindInt64(1, objectStoreIdentifier) != SQLITE_OK
-            || sql.step() != SQLITE_DONE) {
</del><ins>+        auto* sql = cachedStatement(SQL::DeleteObjectStoreInfo, ASCIILiteral(&quot;DELETE FROM ObjectStoreInfo WHERE id = ?;&quot;));
+        if (!sql
+            || sql-&gt;bindInt64(1, objectStoreIdentifier) != SQLITE_OK
+            || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">             LOG_ERROR(&quot;Could not delete object store id %&quot; PRIi64 &quot; from ObjectStoreInfo table (%i) - %s&quot;, objectStoreIdentifier, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Could not delete object store&quot;) };
</span><span class="cx">         }
</span><span class="lines">@@ -920,10 +920,10 @@
</span><span class="cx"> 
</span><span class="cx">     // Delete the ObjectStore's key generator record if there is one.
</span><span class="cx">     {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;DELETE FROM KeyGenerators WHERE objectStoreID = ?;&quot;));
-        if (sql.prepare() != SQLITE_OK
-            || sql.bindInt64(1, objectStoreIdentifier) != SQLITE_OK
-            || sql.step() != SQLITE_DONE) {
</del><ins>+        auto* sql = cachedStatement(SQL::DeleteObjectStoreKeyGenerator, ASCIILiteral(&quot;DELETE FROM KeyGenerators WHERE objectStoreID = ?;&quot;));
+        if (!sql
+            || sql-&gt;bindInt64(1, objectStoreIdentifier) != SQLITE_OK
+            || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">             LOG_ERROR(&quot;Could not delete object store from KeyGenerators table (%i) - %s&quot;, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Could not delete key generator for deleted object store&quot;) };
</span><span class="cx">         }
</span><span class="lines">@@ -931,10 +931,10 @@
</span><span class="cx"> 
</span><span class="cx">     // Delete all associated records
</span><span class="cx">     {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;DELETE FROM Records WHERE objectStoreID = ?;&quot;));
-        if (sql.prepare() != SQLITE_OK
-            || sql.bindInt64(1, objectStoreIdentifier) != SQLITE_OK
-            || sql.step() != SQLITE_DONE) {
</del><ins>+        auto* sql = cachedStatement(SQL::DeleteObjectStoreRecords, ASCIILiteral(&quot;DELETE FROM Records WHERE objectStoreID = ?;&quot;));
+        if (!sql
+            || sql-&gt;bindInt64(1, objectStoreIdentifier) != SQLITE_OK
+            || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">             LOG_ERROR(&quot;Could not delete records for object store %&quot; PRIi64 &quot; (%i) - %s&quot;, objectStoreIdentifier, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Could not delete records for deleted object store&quot;) };
</span><span class="cx">         }
</span><span class="lines">@@ -942,10 +942,10 @@
</span><span class="cx"> 
</span><span class="cx">     // Delete all associated Indexes
</span><span class="cx">     {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;DELETE FROM IndexInfo WHERE objectStoreID = ?;&quot;));
-        if (sql.prepare() != SQLITE_OK
-            || sql.bindInt64(1, objectStoreIdentifier) != SQLITE_OK
-            || sql.step() != SQLITE_DONE) {
</del><ins>+        auto* sql = cachedStatement(SQL::DeleteObjectStoreIndexInfo, ASCIILiteral(&quot;DELETE FROM IndexInfo WHERE objectStoreID = ?;&quot;));
+        if (!sql
+            || sql-&gt;bindInt64(1, objectStoreIdentifier) != SQLITE_OK
+            || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">             LOG_ERROR(&quot;Could not delete index from IndexInfo table (%i) - %s&quot;, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Could not delete IDBIndex for deleted object store&quot;) };
</span><span class="cx">         }
</span><span class="lines">@@ -953,10 +953,10 @@
</span><span class="cx"> 
</span><span class="cx">     // Delete all associated Index records
</span><span class="cx">     {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;DELETE FROM IndexRecords WHERE objectStoreID = ?;&quot;));
-        if (sql.prepare() != SQLITE_OK
-            || sql.bindInt64(1, objectStoreIdentifier) != SQLITE_OK
-            || sql.step() != SQLITE_DONE) {
</del><ins>+        auto* sql = cachedStatement(SQL::DeleteObjectStoreIndexRecords, ASCIILiteral(&quot;DELETE FROM IndexRecords WHERE objectStoreID = ?;&quot;));
+        if (!sql
+            || sql-&gt;bindInt64(1, objectStoreIdentifier) != SQLITE_OK
+            || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">             LOG_ERROR(&quot;Could not delete index records(%i) - %s&quot;, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Could not delete IDBIndex records for deleted object store&quot;) };
</span><span class="cx">         }
</span><span class="lines">@@ -964,9 +964,9 @@
</span><span class="cx"> 
</span><span class="cx">     // Delete all unused Blob URL records.
</span><span class="cx">     {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;DELETE FROM BlobRecords WHERE objectStoreRow NOT IN (SELECT recordID FROM Records)&quot;));
-        if (sql.prepare() != SQLITE_OK
-            || sql.step() != SQLITE_DONE) {
</del><ins>+        auto* sql = cachedStatement(SQL::DeleteObjectStoreBlobRecords, ASCIILiteral(&quot;DELETE FROM BlobRecords WHERE objectStoreRow NOT IN (SELECT recordID FROM Records)&quot;));
+        if (!sql
+            || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">             LOG_ERROR(&quot;Could not delete Blob URL records(%i) - %s&quot;, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Could not delete stored blob records for deleted object store&quot;) };
</span><span class="cx">         }
</span><span class="lines">@@ -1000,11 +1000,11 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;UPDATE ObjectStoreInfo SET name = ? WHERE id = ?;&quot;));
-        if (sql.prepare() != SQLITE_OK
-            || sql.bindText(1, newName) != SQLITE_OK
-            || sql.bindInt64(2, objectStoreIdentifier) != SQLITE_OK
-            || sql.step() != SQLITE_DONE) {
</del><ins>+        auto* sql = cachedStatement(SQL::RenameObjectStore, ASCIILiteral(&quot;UPDATE ObjectStoreInfo SET name = ? WHERE id = ?;&quot;));
+        if (!sql
+            || sql-&gt;bindText(1, newName) != SQLITE_OK
+            || sql-&gt;bindInt64(2, objectStoreIdentifier) != SQLITE_OK
+            || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">             LOG_ERROR(&quot;Could not update name for object store id %&quot; PRIi64 &quot; in ObjectStoreInfo table (%i) - %s&quot;, objectStoreIdentifier, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Could not rename object store&quot;) };
</span><span class="cx">         }
</span><span class="lines">@@ -1033,10 +1033,10 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;DELETE FROM Records WHERE objectStoreID = ?;&quot;));
-        if (sql.prepare() != SQLITE_OK
-            || sql.bindInt64(1, objectStoreID) != SQLITE_OK
-            || sql.step() != SQLITE_DONE) {
</del><ins>+        auto* sql = cachedStatement(SQL::ClearObjectStoreRecords, ASCIILiteral(&quot;DELETE FROM Records WHERE objectStoreID = ?;&quot;));
+        if (!sql
+            || sql-&gt;bindInt64(1, objectStoreID) != SQLITE_OK
+            || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">             LOG_ERROR(&quot;Could not clear records from object store id %&quot; PRIi64 &quot; (%i) - %s&quot;, objectStoreID, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Unable to clear object store&quot;) };
</span><span class="cx">         }
</span><span class="lines">@@ -1043,10 +1043,10 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;DELETE FROM IndexRecords WHERE objectStoreID = ?;&quot;));
-        if (sql.prepare() != SQLITE_OK
-            || sql.bindInt64(1, objectStoreID) != SQLITE_OK
-            || sql.step() != SQLITE_DONE) {
</del><ins>+        auto* sql = cachedStatement(SQL::ClearObjectStoreIndexRecords, ASCIILiteral(&quot;DELETE FROM IndexRecords WHERE objectStoreID = ?;&quot;));
+        if (!sql
+            || sql-&gt;bindInt64(1, objectStoreID) != SQLITE_OK
+            || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">             LOG_ERROR(&quot;Could not delete records from index record store id %&quot; PRIi64 &quot; (%i) - %s&quot;, objectStoreID, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Unable to delete index records while clearing object store&quot;) };
</span><span class="cx">         }
</span><span class="lines">@@ -1079,15 +1079,15 @@
</span><span class="cx">         return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Unable to serialize IDBKeyPath to create index in database&quot;) };
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;INSERT INTO IndexInfo VALUES (?, ?, ?, ?, ?, ?);&quot;));
-    if (sql.prepare() != SQLITE_OK
-        || sql.bindInt64(1, info.identifier()) != SQLITE_OK
-        || sql.bindText(2, info.name()) != SQLITE_OK
-        || sql.bindInt64(3, info.objectStoreIdentifier()) != SQLITE_OK
-        || sql.bindBlob(4, keyPathBlob-&gt;data(), keyPathBlob-&gt;size()) != SQLITE_OK
-        || sql.bindInt(5, info.unique()) != SQLITE_OK
-        || sql.bindInt(6, info.multiEntry()) != SQLITE_OK
-        || sql.step() != SQLITE_DONE) {
</del><ins>+    auto* sql = cachedStatement(SQL::CreateIndexInfo, ASCIILiteral(&quot;INSERT INTO IndexInfo VALUES (?, ?, ?, ?, ?, ?);&quot;));
+    if (!sql
+        || sql-&gt;bindInt64(1, info.identifier()) != SQLITE_OK
+        || sql-&gt;bindText(2, info.name()) != SQLITE_OK
+        || sql-&gt;bindInt64(3, info.objectStoreIdentifier()) != SQLITE_OK
+        || sql-&gt;bindBlob(4, keyPathBlob-&gt;data(), keyPathBlob-&gt;size()) != SQLITE_OK
+        || sql-&gt;bindInt(5, info.unique()) != SQLITE_OK
+        || sql-&gt;bindInt(6, info.multiEntry()) != SQLITE_OK
+        || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">         LOG_ERROR(&quot;Could not add index '%s' to IndexInfo table (%i) - %s&quot;, info.name().utf8().data(), m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">         return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Unable to create index in database&quot;) };
</span><span class="cx">     }
</span><span class="lines">@@ -1108,11 +1108,11 @@
</span><span class="cx"> 
</span><span class="cx">         IDBError error = updateOneIndexForAddRecord(info, key, valueBuffer);
</span><span class="cx">         if (!error.isNull()) {
</span><del>-            SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;DELETE FROM IndexInfo WHERE id = ? AND objectStoreID = ?;&quot;));
-            if (sql.prepare() != SQLITE_OK
-                || sql.bindInt64(1, info.identifier()) != SQLITE_OK
-                || sql.bindInt64(2, info.objectStoreIdentifier()) != SQLITE_OK
-                || sql.step() != SQLITE_DONE) {
</del><ins>+            auto* sql = cachedStatement(SQL::DeleteIndexInfo, ASCIILiteral(&quot;DELETE FROM IndexInfo WHERE id = ? AND objectStoreID = ?;&quot;));
+            if (!sql
+                || sql-&gt;bindInt64(1, info.identifier()) != SQLITE_OK
+                || sql-&gt;bindInt64(2, info.objectStoreIdentifier()) != SQLITE_OK
+                || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">                 LOG_ERROR(&quot;Index creation failed due to uniqueness constraint failure, but there was an error deleting the Index record from the database&quot;);
</span><span class="cx">                 return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Index creation failed due to uniqueness constraint failure, but there was an error deleting the Index record from the database&quot;) };
</span><span class="cx">             }
</span><span class="lines">@@ -1143,16 +1143,16 @@
</span><span class="cx">         return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Unable to serialize IDBKey to check for index record in database&quot;) };
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;SELECT rowid FROM IndexRecords WHERE indexID = ? AND objectStoreID = ? AND key = CAST(? AS TEXT);&quot;));
-    if (sql.prepare() != SQLITE_OK
-        || sql.bindInt64(1, info.identifier()) != SQLITE_OK
-        || sql.bindInt64(2, info.objectStoreIdentifier()) != SQLITE_OK
-        || sql.bindBlob(3, indexKeyBuffer-&gt;data(), indexKeyBuffer-&gt;size()) != SQLITE_OK) {
</del><ins>+    auto* sql = cachedStatement(SQL::HasIndexRecord, ASCIILiteral(&quot;SELECT rowid FROM IndexRecords WHERE indexID = ? AND objectStoreID = ? AND key = CAST(? AS TEXT);&quot;));
+    if (!sql
+        || sql-&gt;bindInt64(1, info.identifier()) != SQLITE_OK
+        || sql-&gt;bindInt64(2, info.objectStoreIdentifier()) != SQLITE_OK
+        || sql-&gt;bindBlob(3, indexKeyBuffer-&gt;data(), indexKeyBuffer-&gt;size()) != SQLITE_OK) {
</ins><span class="cx">         LOG_ERROR(&quot;Error checking for index record in database&quot;);
</span><span class="cx">         return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Error checking for index record in database&quot;) };
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    int sqlResult = sql.step();
</del><ins>+    int sqlResult = sql-&gt;step();
</ins><span class="cx">     if (sqlResult == SQLITE_OK || sqlResult == SQLITE_DONE)
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><span class="lines">@@ -1220,13 +1220,13 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;INSERT INTO IndexRecords VALUES (?, ?, CAST(? AS TEXT), CAST(? AS TEXT));&quot;));
-        if (sql.prepare() != SQLITE_OK
-            || sql.bindInt64(1, indexID) != SQLITE_OK
-            || sql.bindInt64(2, objectStoreID) != SQLITE_OK
-            || sql.bindBlob(3, indexKeyBuffer-&gt;data(), indexKeyBuffer-&gt;size()) != SQLITE_OK
-            || sql.bindBlob(4, valueBuffer-&gt;data(), valueBuffer-&gt;size()) != SQLITE_OK
-            || sql.step() != SQLITE_DONE) {
</del><ins>+        auto* sql = cachedStatement(SQL::PutIndexRecord, ASCIILiteral(&quot;INSERT INTO IndexRecords VALUES (?, ?, CAST(? AS TEXT), CAST(? AS TEXT));&quot;));
+        if (!sql
+            || sql-&gt;bindInt64(1, indexID) != SQLITE_OK
+            || sql-&gt;bindInt64(2, objectStoreID) != SQLITE_OK
+            || sql-&gt;bindBlob(3, indexKeyBuffer-&gt;data(), indexKeyBuffer-&gt;size()) != SQLITE_OK
+            || sql-&gt;bindBlob(4, valueBuffer-&gt;data(), valueBuffer-&gt;size()) != SQLITE_OK
+            || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">             LOG_ERROR(&quot;Could not put index record for index %&quot; PRIi64 &quot; in object store %&quot; PRIi64 &quot; in Records table (%i) - %s&quot;, indexID, objectStoreID, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Error putting index record into database&quot;) };
</span><span class="cx">         }
</span><span class="lines">@@ -1255,11 +1255,11 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;DELETE FROM IndexInfo WHERE id = ? AND objectStoreID = ?;&quot;));
-        if (sql.prepare() != SQLITE_OK
-            || sql.bindInt64(1, indexIdentifier) != SQLITE_OK
-            || sql.bindInt64(2, objectStoreIdentifier) != SQLITE_OK
-            || sql.step() != SQLITE_DONE) {
</del><ins>+        auto* sql = cachedStatement(SQL::DeleteIndexInfo, ASCIILiteral(&quot;DELETE FROM IndexInfo WHERE id = ? AND objectStoreID = ?;&quot;));
+        if (!sql
+            || sql-&gt;bindInt64(1, indexIdentifier) != SQLITE_OK
+            || sql-&gt;bindInt64(2, objectStoreIdentifier) != SQLITE_OK
+            || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">             LOG_ERROR(&quot;Could not delete index id %&quot; PRIi64 &quot; from IndexInfo table (%i) - %s&quot;, objectStoreIdentifier, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Error deleting index from database&quot;) };
</span><span class="cx">         }
</span><span class="lines">@@ -1266,11 +1266,11 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;DELETE FROM IndexRecords WHERE indexID = ? AND objectStoreID = ?;&quot;));
-        if (sql.prepare() != SQLITE_OK
-            || sql.bindInt64(1, indexIdentifier) != SQLITE_OK
-            || sql.bindInt64(2, objectStoreIdentifier) != SQLITE_OK
-            || sql.step() != SQLITE_DONE) {
</del><ins>+        auto* sql = cachedStatement(SQL::DeleteIndexRecords, ASCIILiteral(&quot;DELETE FROM IndexRecords WHERE indexID = ? AND objectStoreID = ?;&quot;));
+        if (!sql
+            || sql-&gt;bindInt64(1, indexIdentifier) != SQLITE_OK
+            || sql-&gt;bindInt64(2, objectStoreIdentifier) != SQLITE_OK
+            || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">             LOG_ERROR(&quot;Could not delete index records for index id %&quot; PRIi64 &quot; from IndexRecords table (%i) - %s&quot;, indexIdentifier, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Error deleting index records from database&quot;) };
</span><span class="cx">         }
</span><span class="lines">@@ -1310,12 +1310,12 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;UPDATE IndexInfo SET name = ? WHERE objectStoreID = ? AND id = ?;&quot;));
-        if (sql.prepare() != SQLITE_OK
-            || sql.bindText(1, newName) != SQLITE_OK
-            || sql.bindInt64(2, objectStoreIdentifier) != SQLITE_OK
-            || sql.bindInt64(3, indexIdentifier) != SQLITE_OK
-            || sql.step() != SQLITE_DONE) {
</del><ins>+        auto* sql = cachedStatement(SQL::RenameIndex, ASCIILiteral(&quot;UPDATE IndexInfo SET name = ? WHERE objectStoreID = ? AND id = ?;&quot;));
+        if (!sql
+            || sql-&gt;bindText(1, newName) != SQLITE_OK
+            || sql-&gt;bindInt64(2, objectStoreIdentifier) != SQLITE_OK
+            || sql-&gt;bindInt64(3, indexIdentifier) != SQLITE_OK
+            || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">             LOG_ERROR(&quot;Could not update name for index id (%&quot; PRIi64 &quot;, %&quot; PRIi64 &quot;) in IndexInfo table (%i) - %s&quot;, objectStoreIdentifier, indexIdentifier, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Could not rename index&quot;) };
</span><span class="cx">         }
</span><span class="lines">@@ -1346,15 +1346,15 @@
</span><span class="cx">         LOG_ERROR(&quot;Unable to serialize IDBKey to check for existence in object store&quot;);
</span><span class="cx">         return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Unable to serialize IDBKey to check for existence in object store&quot;) };
</span><span class="cx">     }
</span><del>-    SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;SELECT key FROM Records WHERE objectStoreID = ? AND key = CAST(? AS TEXT) LIMIT 1;&quot;));
-    if (sql.prepare() != SQLITE_OK
-        || sql.bindInt64(1, objectStoreID) != SQLITE_OK
-        || sql.bindBlob(2, keyBuffer-&gt;data(), keyBuffer-&gt;size()) != SQLITE_OK) {
</del><ins>+    auto* sql = cachedStatement(SQL::KeyExistsInObjectStore, ASCIILiteral(&quot;SELECT key FROM Records WHERE objectStoreID = ? AND key = CAST(? AS TEXT) LIMIT 1;&quot;));
+    if (!sql
+        || sql-&gt;bindInt64(1, objectStoreID) != SQLITE_OK
+        || sql-&gt;bindBlob(2, keyBuffer-&gt;data(), keyBuffer-&gt;size()) != SQLITE_OK) {
</ins><span class="cx">         LOG_ERROR(&quot;Could not get record from object store %&quot; PRIi64 &quot; from Records table (%i) - %s&quot;, objectStoreID, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">         return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Unable to check for existence of IDBKey in object store&quot;) };
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    int sqlResult = sql.step();
</del><ins>+    int sqlResult = sql-&gt;step();
</ins><span class="cx">     if (sqlResult == SQLITE_OK || sqlResult == SQLITE_DONE)
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><span class="lines">@@ -1375,17 +1375,17 @@
</span><span class="cx">     // Gather the set of blob URLs and filenames that are no longer in use.
</span><span class="cx">     HashSet&lt;String&gt; removedBlobFilenames;
</span><span class="cx">     {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;SELECT fileName FROM BlobFiles WHERE blobURL NOT IN (SELECT blobURL FROM BlobRecords)&quot;));
</del><ins>+        auto* sql = cachedStatement(SQL::GetUnusedBlobFilenames, ASCIILiteral(&quot;SELECT fileName FROM BlobFiles WHERE blobURL NOT IN (SELECT blobURL FROM BlobRecords)&quot;));
</ins><span class="cx"> 
</span><del>-        if (sql.prepare() != SQLITE_OK) {
</del><ins>+        if (!sql) {
</ins><span class="cx">             LOG_ERROR(&quot;Error deleting stored blobs (%i) (Could not gather unused blobURLs) - %s&quot;, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Error deleting stored blobs&quot;) };
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        int result = sql.step();
</del><ins>+        int result = sql-&gt;step();
</ins><span class="cx">         while (result == SQLITE_ROW) {
</span><del>-            removedBlobFilenames.add(sql.getColumnText(0));
-            result = sql.step();
</del><ins>+            removedBlobFilenames.add(sql-&gt;getColumnText(0));
+            result = sql-&gt;step();
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         if (result != SQLITE_DONE) {
</span><span class="lines">@@ -1396,10 +1396,10 @@
</span><span class="cx"> 
</span><span class="cx">     // Remove the blob records that are no longer in use.
</span><span class="cx">     if (!removedBlobFilenames.isEmpty()) {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;DELETE FROM BlobFiles WHERE blobURL NOT IN (SELECT blobURL FROM BlobRecords)&quot;));
</del><ins>+        auto* sql = cachedStatement(SQL::DeleteUnusedBlobs, ASCIILiteral(&quot;DELETE FROM BlobFiles WHERE blobURL NOT IN (SELECT blobURL FROM BlobRecords)&quot;));
</ins><span class="cx"> 
</span><del>-        if (sql.prepare() != SQLITE_OK
-            || sql.step() != SQLITE_DONE) {
</del><ins>+        if (!sql
+            || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">             LOG_ERROR(&quot;Error deleting stored blobs (%i) (Could not delete blobFile records) - %s&quot;, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Error deleting stored blobs&quot;) };
</span><span class="cx">         }
</span><span class="lines">@@ -1430,16 +1430,16 @@
</span><span class="cx">     // Get the record ID
</span><span class="cx">     int64_t recordID;
</span><span class="cx">     {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;SELECT recordID FROM Records WHERE objectStoreID = ? AND key = CAST(? AS TEXT);&quot;));
</del><ins>+        auto* sql = cachedStatement(SQL::GetObjectStoreRecordID, ASCIILiteral(&quot;SELECT recordID FROM Records WHERE objectStoreID = ? AND key = CAST(? AS TEXT);&quot;));
</ins><span class="cx"> 
</span><del>-        if (sql.prepare() != SQLITE_OK
-            || sql.bindInt64(1, objectStoreID) != SQLITE_OK
-            || sql.bindBlob(2, keyBuffer-&gt;data(), keyBuffer-&gt;size()) != SQLITE_OK) {
</del><ins>+        if (!sql
+            || sql-&gt;bindInt64(1, objectStoreID) != SQLITE_OK
+            || sql-&gt;bindBlob(2, keyBuffer-&gt;data(), keyBuffer-&gt;size()) != SQLITE_OK) {
</ins><span class="cx">             LOG_ERROR(&quot;Could not delete record from object store %&quot; PRIi64 &quot; (%i) - %s&quot;, objectStoreID, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Failed to delete record from object store&quot;) };
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        int result = sql.step();
</del><ins>+        int result = sql-&gt;step();
</ins><span class="cx"> 
</span><span class="cx">         // If there's no record ID, there's no record to delete.
</span><span class="cx">         if (result == SQLITE_DONE)
</span><span class="lines">@@ -1450,7 +1450,7 @@
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Failed to delete record from object store&quot;) };
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        recordID = sql.getColumnInt64(0);
</del><ins>+        recordID = sql-&gt;getColumnInt64(0);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (recordID &lt; 1) {
</span><span class="lines">@@ -1460,11 +1460,11 @@
</span><span class="cx"> 
</span><span class="cx">     // Delete the blob records for this object store record.
</span><span class="cx">     {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;DELETE FROM BlobRecords WHERE objectStoreRow = ?;&quot;));
</del><ins>+        auto* sql = cachedStatement(SQL::DeleteBlobRecord, ASCIILiteral(&quot;DELETE FROM BlobRecords WHERE objectStoreRow = ?;&quot;));
</ins><span class="cx"> 
</span><del>-        if (sql.prepare() != SQLITE_OK
-            || sql.bindInt64(1, recordID) != SQLITE_OK
-            || sql.step() != SQLITE_DONE) {
</del><ins>+        if (!sql
+            || sql-&gt;bindInt64(1, recordID) != SQLITE_OK
+            || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">             LOG_ERROR(&quot;Could not delete record from object store %&quot; PRIi64 &quot; (%i) (Could not delete BlobRecords records) - %s&quot;, objectStoreID, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Failed to delete record from object store&quot;) };
</span><span class="cx">         }
</span><span class="lines">@@ -1476,12 +1476,12 @@
</span><span class="cx"> 
</span><span class="cx">     // Delete record from object store
</span><span class="cx">     {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;DELETE FROM Records WHERE objectStoreID = ? AND key = CAST(? AS TEXT);&quot;));
</del><ins>+        auto* sql = cachedStatement(SQL::DeleteObjectStoreRecord, ASCIILiteral(&quot;DELETE FROM Records WHERE objectStoreID = ? AND key = CAST(? AS TEXT);&quot;));
</ins><span class="cx"> 
</span><del>-        if (sql.prepare() != SQLITE_OK
-            || sql.bindInt64(1, objectStoreID) != SQLITE_OK
-            || sql.bindBlob(2, keyBuffer-&gt;data(), keyBuffer-&gt;size()) != SQLITE_OK
-            || sql.step() != SQLITE_DONE) {
</del><ins>+        if (!sql
+            || sql-&gt;bindInt64(1, objectStoreID) != SQLITE_OK
+            || sql-&gt;bindBlob(2, keyBuffer-&gt;data(), keyBuffer-&gt;size()) != SQLITE_OK
+            || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">             LOG_ERROR(&quot;Could not delete record from object store %&quot; PRIi64 &quot; (%i) - %s&quot;, objectStoreID, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Failed to delete record from object store&quot;) };
</span><span class="cx">         }
</span><span class="lines">@@ -1489,12 +1489,12 @@
</span><span class="cx"> 
</span><span class="cx">     // Delete record from indexes store
</span><span class="cx">     {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;DELETE FROM IndexRecords WHERE objectStoreID = ? AND value = CAST(? AS TEXT);&quot;));
</del><ins>+        auto* sql = cachedStatement(SQL::DeleteObjectStoreIndexRecord, ASCIILiteral(&quot;DELETE FROM IndexRecords WHERE objectStoreID = ? AND value = CAST(? AS TEXT);&quot;));
</ins><span class="cx"> 
</span><del>-        if (sql.prepare() != SQLITE_OK
-            || sql.bindInt64(1, objectStoreID) != SQLITE_OK
-            || sql.bindBlob(2, keyBuffer-&gt;data(), keyBuffer-&gt;size()) != SQLITE_OK
-            || sql.step() != SQLITE_DONE) {
</del><ins>+        if (!sql
+            || sql-&gt;bindInt64(1, objectStoreID) != SQLITE_OK
+            || sql-&gt;bindBlob(2, keyBuffer-&gt;data(), keyBuffer-&gt;size()) != SQLITE_OK
+            || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">             LOG_ERROR(&quot;Could not delete record from indexes for object store %&quot; PRIi64 &quot; (%i) - %s&quot;, objectStoreID, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Failed to delete index entries for object store record&quot;) };
</span><span class="cx">         }
</span><span class="lines">@@ -1606,12 +1606,12 @@
</span><span class="cx">     if (!error.isNull() &amp;&amp; anyRecordsSucceeded) {
</span><span class="cx">         RefPtr&lt;SharedBuffer&gt; keyBuffer = serializeIDBKeyData(key);
</span><span class="cx"> 
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;DELETE FROM IndexRecords WHERE objectStoreID = ? AND value = CAST(? AS TEXT);&quot;));
</del><ins>+        auto* sql = cachedStatement(SQL::DeleteObjectStoreIndexRecord, ASCIILiteral(&quot;DELETE FROM IndexRecords WHERE objectStoreID = ? AND value = CAST(? AS TEXT);&quot;));
</ins><span class="cx"> 
</span><del>-        if (sql.prepare() != SQLITE_OK
-            || sql.bindInt64(1, info.identifier()) != SQLITE_OK
-            || sql.bindBlob(2, keyBuffer-&gt;data(), keyBuffer-&gt;size()) != SQLITE_OK
-            || sql.step() != SQLITE_DONE) {
</del><ins>+        if (!sql
+            || sql-&gt;bindInt64(1, info.identifier()) != SQLITE_OK
+            || sql-&gt;bindBlob(2, keyBuffer-&gt;data(), keyBuffer-&gt;size()) != SQLITE_OK
+            || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">             LOG_ERROR(&quot;Adding one Index record failed, but failed to remove all others that previously succeeded&quot;);
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Adding one Index record failed, but failed to remove all others that previously succeeded&quot;) };
</span><span class="cx">         }
</span><span class="lines">@@ -1647,12 +1647,12 @@
</span><span class="cx"> 
</span><span class="cx">     int64_t recordID = 0;
</span><span class="cx">     {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;INSERT INTO Records VALUES (?, CAST(? AS TEXT), ?, NULL);&quot;));
-        if (sql.prepare() != SQLITE_OK
-            || sql.bindInt64(1, objectStoreInfo.identifier()) != SQLITE_OK
-            || sql.bindBlob(2, keyBuffer-&gt;data(), keyBuffer-&gt;size()) != SQLITE_OK
-            || sql.bindBlob(3, value.data().data()-&gt;data(), value.data().data()-&gt;size()) != SQLITE_OK
-            || sql.step() != SQLITE_DONE) {
</del><ins>+        auto* sql = cachedStatement(SQL::AddObjectStoreRecord, ASCIILiteral(&quot;INSERT INTO Records VALUES (?, CAST(? AS TEXT), ?, NULL);&quot;));
+        if (!sql
+            || sql-&gt;bindInt64(1, objectStoreInfo.identifier()) != SQLITE_OK
+            || sql-&gt;bindBlob(2, keyBuffer-&gt;data(), keyBuffer-&gt;size()) != SQLITE_OK
+            || sql-&gt;bindBlob(3, value.data().data()-&gt;data(), value.data().data()-&gt;size()) != SQLITE_OK
+            || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">             LOG_ERROR(&quot;Could not put record for object store %&quot; PRIi64 &quot; in Records table (%i) - %s&quot;, objectStoreInfo.identifier(), m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Unable to store record in object store&quot;) };
</span><span class="cx">         }
</span><span class="lines">@@ -1663,11 +1663,11 @@
</span><span class="cx">     auto error = updateAllIndexesForAddRecord(objectStoreInfo, keyData, value.data());
</span><span class="cx"> 
</span><span class="cx">     if (!error.isNull()) {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;DELETE FROM Records WHERE objectStoreID = ? AND key = CAST(? AS TEXT);&quot;));
-        if (sql.prepare() != SQLITE_OK
-            || sql.bindInt64(1, objectStoreInfo.identifier()) != SQLITE_OK
-            || sql.bindBlob(2, keyBuffer-&gt;data(), keyBuffer-&gt;size()) != SQLITE_OK
-            || sql.step() != SQLITE_DONE) {
</del><ins>+        auto* sql = cachedStatement(SQL::DeleteObjectStoreRecord, ASCIILiteral(&quot;DELETE FROM Records WHERE objectStoreID = ? AND key = CAST(? AS TEXT);&quot;));
+        if (!sql
+            || sql-&gt;bindInt64(1, objectStoreInfo.identifier()) != SQLITE_OK
+            || sql-&gt;bindBlob(2, keyBuffer-&gt;data(), keyBuffer-&gt;size()) != SQLITE_OK
+            || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">             LOG_ERROR(&quot;Indexing new object store record failed, but unable to remove the object store record itself&quot;);
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Indexing new object store record failed, but unable to remove the object store record itself&quot;) };
</span><span class="cx">         }
</span><span class="lines">@@ -1680,11 +1680,11 @@
</span><span class="cx">     for (size_t i = 0; i &lt; blobURLs.size(); ++i) {
</span><span class="cx">         auto&amp; url = blobURLs[i];
</span><span class="cx">         {
</span><del>-            SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;INSERT INTO BlobRecords VALUES (?, ?);&quot;));
-            if (sql.prepare() != SQLITE_OK
-                || sql.bindInt64(1, recordID) != SQLITE_OK
-                || sql.bindText(2, url) != SQLITE_OK
-                || sql.step() != SQLITE_DONE) {
</del><ins>+            auto* sql = cachedStatement(SQL::AddBlobRecord, ASCIILiteral(&quot;INSERT INTO BlobRecords VALUES (?, ?);&quot;));
+            if (!sql
+                || sql-&gt;bindInt64(1, recordID) != SQLITE_OK
+                || sql-&gt;bindText(2, url) != SQLITE_OK
+                || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">                 LOG_ERROR(&quot;Unable to record Blob record in database&quot;);
</span><span class="cx">                 return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Unable to record Blob record in database&quot;) };
</span><span class="cx">             }
</span><span class="lines">@@ -1693,14 +1693,14 @@
</span><span class="cx"> 
</span><span class="cx">         // If we already have a file for this blobURL, nothing left to do.
</span><span class="cx">         {
</span><del>-            SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;SELECT fileName FROM BlobFiles WHERE blobURL = ?;&quot;));
-            if (sql.prepare() != SQLITE_OK
-                || sql.bindText(1, url) != SQLITE_OK) {
</del><ins>+            auto* sql = cachedStatement(SQL::BlobFilenameForBlobURL, ASCIILiteral(&quot;SELECT fileName FROM BlobFiles WHERE blobURL = ?;&quot;));
+            if (!sql
+                || sql-&gt;bindText(1, url) != SQLITE_OK) {
</ins><span class="cx">                 LOG_ERROR(&quot;Unable to examine Blob filenames in database&quot;);
</span><span class="cx">                 return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Unable to examine Blob filenames in database&quot;) };
</span><span class="cx">             }
</span><span class="cx"> 
</span><del>-            int result = sql.step();
</del><ins>+            int result = sql-&gt;step();
</ins><span class="cx">             if (result != SQLITE_ROW &amp;&amp; result != SQLITE_DONE) {
</span><span class="cx">                 LOG_ERROR(&quot;Unable to examine Blob filenames in database&quot;);
</span><span class="cx">                 return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Unable to examine Blob filenames in database&quot;) };
</span><span class="lines">@@ -1713,11 +1713,11 @@
</span><span class="cx">         // We don't already have a file for this blobURL, so commit our file as a unique filename
</span><span class="cx">         String storedFilename = String::format(&quot;%&quot; PRId64 &quot;.blob&quot;, potentialFileNameInteger);
</span><span class="cx">         {
</span><del>-            SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;INSERT INTO BlobFiles VALUES (?, ?);&quot;));
-            if (sql.prepare() != SQLITE_OK
-                || sql.bindText(1, url) != SQLITE_OK
-                || sql.bindText(2, storedFilename) != SQLITE_OK
-                || sql.step() != SQLITE_DONE) {
</del><ins>+            auto* sql = cachedStatement(SQL::AddBlobFilename, ASCIILiteral(&quot;INSERT INTO BlobFiles VALUES (?, ?);&quot;));
+            if (!sql
+                || sql-&gt;bindText(1, url) != SQLITE_OK
+                || sql-&gt;bindText(2, storedFilename) != SQLITE_OK
+                || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">                 LOG_ERROR(&quot;Unable to record Blob file record in database&quot;);
</span><span class="cx">                 return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Unable to record Blob file record in database&quot;) };
</span><span class="cx">             }
</span><span class="lines">@@ -1737,14 +1737,14 @@
</span><span class="cx"> 
</span><span class="cx">     HashSet&lt;String&gt; blobURLSet;
</span><span class="cx">     {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;SELECT blobURL FROM BlobRecords WHERE objectStoreRow = ?&quot;));
-        if (sql.prepare() != SQLITE_OK
-            || sql.bindInt64(1, objectStoreRecord) != SQLITE_OK) {
</del><ins>+        auto* sql = cachedStatement(SQL::GetBlobURL, ASCIILiteral(&quot;SELECT blobURL FROM BlobRecords WHERE objectStoreRow = ?&quot;));
+        if (!sql
+            || sql-&gt;bindInt64(1, objectStoreRecord) != SQLITE_OK) {
</ins><span class="cx">             LOG_ERROR(&quot;Could not prepare statement to fetch blob URLs for object store record (%i) - %s&quot;, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Failed to look up blobURL records in object store by key range&quot;) };
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        int sqlResult = sql.step();
</del><ins>+        int sqlResult = sql-&gt;step();
</ins><span class="cx">         if (sqlResult == SQLITE_OK || sqlResult == SQLITE_DONE) {
</span><span class="cx">             // There are no blobURLs in the database for this object store record.
</span><span class="cx">             return { };
</span><span class="lines">@@ -1751,8 +1751,8 @@
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         while (sqlResult == SQLITE_ROW) {
</span><del>-            blobURLSet.add(sql.getColumnText(0));
-            sqlResult = sql.step();
</del><ins>+            blobURLSet.add(sql-&gt;getColumnText(0));
+            sqlResult = sql-&gt;step();
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         if (sqlResult != SQLITE_DONE) {
</span><span class="lines">@@ -1764,14 +1764,14 @@
</span><span class="cx">     ASSERT(!blobURLSet.isEmpty());
</span><span class="cx">     String databaseDirectory = fullDatabaseDirectory();
</span><span class="cx">     for (auto&amp; blobURL : blobURLSet) {
</span><del>-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;SELECT fileName FROM BlobFiles WHERE blobURL = ?&quot;));
-        if (sql.prepare() != SQLITE_OK
-            || sql.bindText(1, blobURL) != SQLITE_OK) {
</del><ins>+        auto* sql = cachedStatement(SQL::BlobFilenameForBlobURL, ASCIILiteral(&quot;SELECT fileName FROM BlobFiles WHERE blobURL = ?;&quot;));
+        if (!sql
+            || sql-&gt;bindText(1, blobURL) != SQLITE_OK) {
</ins><span class="cx">             LOG_ERROR(&quot;Could not prepare statement to fetch blob filename for object store record (%i) - %s&quot;, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Failed to look up blobURL records in object store by key range&quot;) };
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        if (sql.step() != SQLITE_ROW) {
</del><ins>+        if (sql-&gt;step() != SQLITE_ROW) {
</ins><span class="cx">             LOG_ERROR(&quot;Entry for blob filename for blob url %s does not exist (%i) - %s&quot;, blobURL.utf8().data(), m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Failed to look up blobURL records in object store by key range&quot;) };
</span><span class="cx">         }
</span><span class="lines">@@ -1778,7 +1778,7 @@
</span><span class="cx"> 
</span><span class="cx">         blobURLs.append(blobURL);
</span><span class="cx"> 
</span><del>-        String fileName = sql.getColumnText(0);
</del><ins>+        String fileName = sql-&gt;getColumnText(0);
</ins><span class="cx">         blobFilePaths.append(pathByAppendingComponent(databaseDirectory, fileName));
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -1819,37 +1819,33 @@
</span><span class="cx">     int64_t recordID = 0;
</span><span class="cx">     ThreadSafeDataBuffer resultBuffer;
</span><span class="cx">     {
</span><del>-        static NeverDestroyed&lt;const ASCIILiteral&gt; lowerOpenUpperOpen(&quot;SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key &gt; CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key;&quot;);
-        static NeverDestroyed&lt;const ASCIILiteral&gt; lowerOpenUpperClosed(&quot;SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key &gt; CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key;&quot;);
-        static NeverDestroyed&lt;const ASCIILiteral&gt; lowerClosedUpperOpen(&quot;SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key;&quot;);
-        static NeverDestroyed&lt;const ASCIILiteral&gt; lowerClosedUpperClosed(&quot;SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key;&quot;);
</del><ins>+        static NeverDestroyed&lt;ASCIILiteral&gt; lowerOpenUpperOpen(&quot;SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key &gt; CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key;&quot;);
+        static NeverDestroyed&lt;ASCIILiteral&gt; lowerOpenUpperClosed(&quot;SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key &gt; CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key;&quot;);
+        static NeverDestroyed&lt;ASCIILiteral&gt; lowerClosedUpperOpen(&quot;SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key;&quot;);
+        static NeverDestroyed&lt;ASCIILiteral&gt; lowerClosedUpperClosed(&quot;SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key;&quot;);
</ins><span class="cx"> 
</span><del>-        const ASCIILiteral* query = nullptr;
-
</del><ins>+        SQLiteStatement* sql = nullptr;
</ins><span class="cx">         if (keyRange.lowerOpen) {
</span><span class="cx">             if (keyRange.upperOpen)
</span><del>-                query = &amp;lowerOpenUpperOpen.get();
</del><ins>+                sql = cachedStatement(SQL::GetValueRecordsLowerOpenUpperOpen, lowerOpenUpperOpen.get());
</ins><span class="cx">             else
</span><del>-                query = &amp;lowerOpenUpperClosed.get();
</del><ins>+                sql = cachedStatement(SQL::GetValueRecordsLowerOpenUpperClosed, lowerOpenUpperClosed.get());
</ins><span class="cx">         } else {
</span><span class="cx">             if (keyRange.upperOpen)
</span><del>-                query = &amp;lowerClosedUpperOpen.get();
</del><ins>+                sql = cachedStatement(SQL::GetValueRecordsLowerClosedUpperOpen, lowerClosedUpperOpen.get());
</ins><span class="cx">             else
</span><del>-                query = &amp;lowerClosedUpperClosed.get();
</del><ins>+                sql = cachedStatement(SQL::GetValueRecordsLowerClosedUpperClosed, lowerClosedUpperClosed.get());
</ins><span class="cx">         }
</span><span class="cx"> 
</span><del>-        ASSERT(query);
-
-        SQLiteStatement sql(*m_sqliteDB, *query);
-        if (sql.prepare() != SQLITE_OK
-            || sql.bindInt64(1, objectStoreID) != SQLITE_OK
-            || sql.bindBlob(2, lowerBuffer-&gt;data(), lowerBuffer-&gt;size()) != SQLITE_OK
-            || sql.bindBlob(3, upperBuffer-&gt;data(), upperBuffer-&gt;size()) != SQLITE_OK) {
</del><ins>+        if (!sql
+            || sql-&gt;bindInt64(1, objectStoreID) != SQLITE_OK
+            || sql-&gt;bindBlob(2, lowerBuffer-&gt;data(), lowerBuffer-&gt;size()) != SQLITE_OK
+            || sql-&gt;bindBlob(3, upperBuffer-&gt;data(), upperBuffer-&gt;size()) != SQLITE_OK) {
</ins><span class="cx">             LOG_ERROR(&quot;Could not get key range record from object store %&quot; PRIi64 &quot; from Records table (%i) - %s&quot;, objectStoreID, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">             return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Failed to look up record in object store by key range&quot;) };
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        int sqlResult = sql.step();
</del><ins>+        int sqlResult = sql-&gt;step();
</ins><span class="cx"> 
</span><span class="cx">         if (sqlResult == SQLITE_OK || sqlResult == SQLITE_DONE) {
</span><span class="cx">             // There was no record for the key in the database.
</span><span class="lines">@@ -1862,10 +1858,10 @@
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         Vector&lt;uint8_t&gt; buffer;
</span><del>-        sql.getColumnBlobAsVector(0, buffer);
</del><ins>+        sql-&gt;getColumnBlobAsVector(0, buffer);
</ins><span class="cx">         resultBuffer = ThreadSafeDataBuffer::adoptVector(buffer);
</span><span class="cx"> 
</span><del>-        recordID = sql.getColumnInt64(1);
</del><ins>+        recordID = sql-&gt;getColumnInt64(1);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     ASSERT(recordID);
</span><span class="lines">@@ -1885,38 +1881,38 @@
</span><span class="cx">     return getAllRecordsData.indexIdentifier ? getAllIndexRecords(transactionIdentifier, getAllRecordsData, result) : getAllObjectStoreRecords(transactionIdentifier, getAllRecordsData, result);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static const ASCIILiteral&amp; queryForGetAllObjectStoreRecords(const IDBGetAllRecordsData&amp; getAllRecordsData)
</del><ins>+SQLiteStatement* SQLiteIDBBackingStore::cachedStatementForGetAllObjectStoreRecords(const IDBGetAllRecordsData&amp; getAllRecordsData)
</ins><span class="cx"> {
</span><del>-    static NeverDestroyed&lt;const ASCIILiteral&gt; lowerOpenUpperOpenKey(&quot;SELECT key FROM Records WHERE objectStoreID = ? AND key &gt; CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key;&quot;);
-    static NeverDestroyed&lt;const ASCIILiteral&gt; lowerOpenUpperClosedKey(&quot;SELECT key FROM Records WHERE objectStoreID = ? AND key &gt; CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key;&quot;);
-    static NeverDestroyed&lt;const ASCIILiteral&gt; lowerClosedUpperOpenKey(&quot;SELECT key FROM Records WHERE objectStoreID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key;&quot;);
-    static NeverDestroyed&lt;const ASCIILiteral&gt; lowerClosedUpperClosedKey(&quot;SELECT key FROM Records WHERE objectStoreID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key;&quot;);
-    static NeverDestroyed&lt;const ASCIILiteral&gt; lowerOpenUpperOpenValue(&quot;SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key &gt; CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key;&quot;);
-    static NeverDestroyed&lt;const ASCIILiteral&gt; lowerOpenUpperClosedValue(&quot;SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key &gt; CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key;&quot;);
-    static NeverDestroyed&lt;const ASCIILiteral&gt; lowerClosedUpperOpenValue(&quot;SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key;&quot;);
-    static NeverDestroyed&lt;const ASCIILiteral&gt; lowerClosedUpperClosedValue(&quot;SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key;&quot;);
</del><ins>+    static NeverDestroyed&lt;ASCIILiteral&gt; lowerOpenUpperOpenKey(&quot;SELECT key FROM Records WHERE objectStoreID = ? AND key &gt; CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key;&quot;);
+    static NeverDestroyed&lt;ASCIILiteral&gt; lowerOpenUpperClosedKey(&quot;SELECT key FROM Records WHERE objectStoreID = ? AND key &gt; CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key;&quot;);
+    static NeverDestroyed&lt;ASCIILiteral&gt; lowerClosedUpperOpenKey(&quot;SELECT key FROM Records WHERE objectStoreID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key;&quot;);
+    static NeverDestroyed&lt;ASCIILiteral&gt; lowerClosedUpperClosedKey(&quot;SELECT key FROM Records WHERE objectStoreID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key;&quot;);
+    static NeverDestroyed&lt;ASCIILiteral&gt; lowerOpenUpperOpenValue(&quot;SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key &gt; CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key;&quot;);
+    static NeverDestroyed&lt;ASCIILiteral&gt; lowerOpenUpperClosedValue(&quot;SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key &gt; CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key;&quot;);
+    static NeverDestroyed&lt;ASCIILiteral&gt; lowerClosedUpperOpenValue(&quot;SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt; CAST(? AS TEXT) ORDER BY key;&quot;);
+    static NeverDestroyed&lt;ASCIILiteral&gt; lowerClosedUpperClosedValue(&quot;SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key &gt;= CAST(? AS TEXT) AND key &lt;= CAST(? AS TEXT) ORDER BY key;&quot;);
</ins><span class="cx"> 
</span><span class="cx">     if (getAllRecordsData.getAllType == IndexedDB::GetAllType::Keys) {
</span><span class="cx">         if (getAllRecordsData.keyRangeData.lowerOpen) {
</span><span class="cx">             if (getAllRecordsData.keyRangeData.upperOpen)
</span><del>-                return lowerOpenUpperOpenKey.get();
-            return lowerOpenUpperClosedKey.get();
</del><ins>+                return cachedStatement(SQL::GetAllKeyRecordsLowerOpenUpperOpen, lowerOpenUpperOpenKey.get());
+            return cachedStatement(SQL::GetAllKeyRecordsLowerOpenUpperClosed, lowerOpenUpperClosedKey.get());
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         if (getAllRecordsData.keyRangeData.upperOpen)
</span><del>-            return lowerClosedUpperOpenKey.get();
-        return lowerClosedUpperClosedKey.get();
</del><ins>+            return cachedStatement(SQL::GetAllKeyRecordsLowerClosedUpperOpen, lowerClosedUpperOpenKey.get());
+        return cachedStatement(SQL::GetAllKeyRecordsLowerClosedUpperClosed, lowerClosedUpperClosedKey.get());
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (getAllRecordsData.keyRangeData.lowerOpen) {
</span><span class="cx">         if (getAllRecordsData.keyRangeData.upperOpen)
</span><del>-            return lowerOpenUpperOpenValue.get();
-        return lowerOpenUpperClosedValue.get();
</del><ins>+            return cachedStatement(SQL::GetValueRecordsLowerOpenUpperOpen, lowerOpenUpperOpenValue.get());
+        return cachedStatement(SQL::GetValueRecordsLowerOpenUpperClosed, lowerOpenUpperClosedValue.get());
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (getAllRecordsData.keyRangeData.upperOpen)
</span><del>-        return lowerClosedUpperOpenValue.get();
-    return lowerClosedUpperClosedValue.get();
</del><ins>+        return cachedStatement(SQL::GetValueRecordsLowerClosedUpperOpen, lowerClosedUpperOpenValue.get());
+    return cachedStatement(SQL::GetValueRecordsLowerClosedUpperClosed, lowerClosedUpperClosedValue.get());
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> IDBError SQLiteIDBBackingStore::getAllObjectStoreRecords(const IDBResourceIdentifier&amp; transactionIdentifier, const IDBGetAllRecordsData&amp; getAllRecordsData, IDBGetAllResult&amp; result)
</span><span class="lines">@@ -1950,11 +1946,11 @@
</span><span class="cx">         return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Unable to serialize upper IDBKey in lookup range&quot;) };
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    SQLiteStatement sql(*m_sqliteDB, queryForGetAllObjectStoreRecords(getAllRecordsData));
-    if (sql.prepare() != SQLITE_OK
-        || sql.bindInt64(1, getAllRecordsData.objectStoreIdentifier) != SQLITE_OK
-        || sql.bindBlob(2, lowerBuffer-&gt;data(), lowerBuffer-&gt;size()) != SQLITE_OK
-        || sql.bindBlob(3, upperBuffer-&gt;data(), upperBuffer-&gt;size()) != SQLITE_OK) {
</del><ins>+    auto* sql = cachedStatementForGetAllObjectStoreRecords(getAllRecordsData);
+    if (!sql
+        || sql-&gt;bindInt64(1, getAllRecordsData.objectStoreIdentifier) != SQLITE_OK
+        || sql-&gt;bindBlob(2, lowerBuffer-&gt;data(), lowerBuffer-&gt;size()) != SQLITE_OK
+        || sql-&gt;bindBlob(3, upperBuffer-&gt;data(), upperBuffer-&gt;size()) != SQLITE_OK) {
</ins><span class="cx">         LOG_ERROR(&quot;Could not get key range record from object store %&quot; PRIi64 &quot; from Records table (%i) - %s&quot;, getAllRecordsData.objectStoreIdentifier, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">         return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Failed to look up record in object store by key range&quot;) };
</span><span class="cx">     }
</span><span class="lines">@@ -1967,16 +1963,16 @@
</span><span class="cx">     else
</span><span class="cx">         targetResults = std::numeric_limits&lt;uint32_t&gt;::max();
</span><span class="cx"> 
</span><del>-    int sqlResult = sql.step();
</del><ins>+    int sqlResult = sql-&gt;step();
</ins><span class="cx">     uint32_t returnedResults = 0;
</span><span class="cx"> 
</span><span class="cx">     while (sqlResult == SQLITE_ROW &amp;&amp; returnedResults &lt; targetResults) {
</span><span class="cx">         if (getAllRecordsData.getAllType == IndexedDB::GetAllType::Values) {
</span><span class="cx">             Vector&lt;uint8_t&gt; buffer;
</span><del>-            sql.getColumnBlobAsVector(0, buffer);
</del><ins>+            sql-&gt;getColumnBlobAsVector(0, buffer);
</ins><span class="cx">             ThreadSafeDataBuffer resultBuffer = ThreadSafeDataBuffer::adoptVector(buffer);
</span><span class="cx"> 
</span><del>-            auto recordID = sql.getColumnInt64(1);
</del><ins>+            auto recordID = sql-&gt;getColumnInt64(1);
</ins><span class="cx"> 
</span><span class="cx">             ASSERT(recordID);
</span><span class="cx">             Vector&lt;String&gt; blobURLs, blobFilePaths;
</span><span class="lines">@@ -1990,7 +1986,7 @@
</span><span class="cx">         } else {
</span><span class="cx">             Vector&lt;uint8_t&gt; keyData;
</span><span class="cx">             IDBKeyData key;
</span><del>-            sql.getColumnBlobAsVector(0, keyData);
</del><ins>+            sql-&gt;getColumnBlobAsVector(0, keyData);
</ins><span class="cx"> 
</span><span class="cx">             if (!deserializeIDBKeyData(keyData.data(), keyData.size(), key)) {
</span><span class="cx">                 LOG_ERROR(&quot;Unable to deserialize key data from database while getting all key records&quot;);
</span><span class="lines">@@ -2001,7 +1997,7 @@
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         ++returnedResults;
</span><del>-        sqlResult = sql.step();
</del><ins>+        sqlResult = sql-&gt;step();
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (sqlResult == SQLITE_OK || sqlResult == SQLITE_DONE || sqlResult == SQLITE_ROW) {
</span><span class="lines">@@ -2126,19 +2122,19 @@
</span><span class="cx"> 
</span><span class="cx"> IDBError SQLiteIDBBackingStore::uncheckedGetKeyGeneratorValue(int64_t objectStoreID, uint64_t&amp; outValue)
</span><span class="cx"> {
</span><del>-    SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;SELECT currentKey FROM KeyGenerators WHERE objectStoreID = ?;&quot;));
-    if (sql.prepare() != SQLITE_OK
-        || sql.bindInt64(1, objectStoreID) != SQLITE_OK) {
</del><ins>+    auto* sql = cachedStatement(SQL::GetKeyGeneratorValue, ASCIILiteral(&quot;SELECT currentKey FROM KeyGenerators WHERE objectStoreID = ?;&quot;));
+    if (!sql
+        || sql-&gt;bindInt64(1, objectStoreID) != SQLITE_OK) {
</ins><span class="cx">         LOG_ERROR(&quot;Could not retrieve currentKey from KeyGenerators table (%i) - %s&quot;, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">         return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Error getting current key generator value from database&quot;) };
</span><span class="cx">     }
</span><del>-    int result = sql.step();
</del><ins>+    int result = sql-&gt;step();
</ins><span class="cx">     if (result != SQLITE_ROW) {
</span><span class="cx">         LOG_ERROR(&quot;Could not retreive key generator value for object store, but it should be there.&quot;);
</span><span class="cx">         return { IDBDatabaseException::UnknownError, ASCIILiteral(&quot;Error finding current key generator value in database&quot;) };
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    int64_t value = sql.getColumnInt64(0);
</del><ins>+    int64_t value = sql-&gt;getColumnInt64(0);
</ins><span class="cx">     if (value &lt; 0)
</span><span class="cx">         return { IDBDatabaseException::ConstraintError, &quot;Current key generator value from database is invalid&quot; };
</span><span class="cx"> 
</span><span class="lines">@@ -2148,11 +2144,11 @@
</span><span class="cx"> 
</span><span class="cx"> IDBError SQLiteIDBBackingStore::uncheckedSetKeyGeneratorValue(int64_t objectStoreID, uint64_t value)
</span><span class="cx"> {
</span><del>-    SQLiteStatement sql(*m_sqliteDB, ASCIILiteral(&quot;INSERT INTO KeyGenerators VALUES (?, ?);&quot;));
-    if (sql.prepare() != SQLITE_OK
-        || sql.bindInt64(1, objectStoreID) != SQLITE_OK
-        || sql.bindInt64(2, value) != SQLITE_OK
-        || sql.step() != SQLITE_DONE) {
</del><ins>+    auto* sql = cachedStatement(SQL::SetKeyGeneratorValue, ASCIILiteral(&quot;INSERT INTO KeyGenerators VALUES (?, ?);&quot;));
+    if (!sql
+        || sql-&gt;bindInt64(1, objectStoreID) != SQLITE_OK
+        || sql-&gt;bindInt64(2, value) != SQLITE_OK
+        || sql-&gt;step() != SQLITE_DONE) {
</ins><span class="cx">         LOG_ERROR(&quot;Could not update key generator value (%i) - %s&quot;, m_sqliteDB-&gt;lastError(), m_sqliteDB-&gt;lastErrorMsg());
</span><span class="cx">         return { IDBDatabaseException::ConstraintError, &quot;Error storing new key generator value in database&quot; };
</span><span class="cx">     }
</span><span class="lines">@@ -2354,10 +2350,8 @@
</span><span class="cx">             LOG_ERROR(&quot;Error deleting blob file %s&quot;, fullPath.utf8().data());
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (m_sqliteDB) {
-        m_sqliteDB-&gt;close();
-        m_sqliteDB = nullptr;
-    }
</del><ins>+    if (m_sqliteDB)
+        closeSQLiteDB();
</ins><span class="cx"> 
</span><span class="cx">     SQLiteFileSystem::deleteDatabaseFile(dbFilename);
</span><span class="cx">     SQLiteFileSystem::deleteEmptyDatabaseDirectory(fullDatabaseDirectory());
</span><span class="lines">@@ -2370,6 +2364,39 @@
</span><span class="cx">     m_cursors.remove(cursor.identifier());
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+SQLiteStatement* SQLiteIDBBackingStore::cachedStatement(SQLiteIDBBackingStore::SQL sql, const char* statement)
+{
+    if (sql &gt;= SQL::Count) {
+        LOG_ERROR(&quot;Invalid SQL statement ID passed to cachedStatement()&quot;);
+        return nullptr;
+    }
+
+    if (m_cachedStatements[static_cast&lt;size_t&gt;(sql)]) {
+        if (m_cachedStatements[static_cast&lt;size_t&gt;(sql)]-&gt;reset() == SQLITE_OK)
+            return m_cachedStatements[static_cast&lt;size_t&gt;(sql)].get();
+        m_cachedStatements[static_cast&lt;size_t&gt;(sql)] = nullptr;
+    }
+
+    if (m_sqliteDB) {
+        m_cachedStatements[static_cast&lt;size_t&gt;(sql)] = std::make_unique&lt;SQLiteStatement&gt;(*m_sqliteDB, statement);
+        if (m_cachedStatements[static_cast&lt;size_t&gt;(sql)]-&gt;prepare() != SQLITE_OK)
+            m_cachedStatements[static_cast&lt;size_t&gt;(sql)] = nullptr;
+    }
+
+    return m_cachedStatements[static_cast&lt;size_t&gt;(sql)].get();
+}
+
+void SQLiteIDBBackingStore::closeSQLiteDB()
+{
+    ASSERT(m_sqliteDB);
+
+    for (size_t i = 0; i &lt; static_cast&lt;int&gt;(SQL::Count); ++i)
+        m_cachedStatements[i] = nullptr;
+
+    m_sqliteDB-&gt;close();
+    m_sqliteDB = nullptr;
+}
+
</ins><span class="cx"> } // namespace IDBServer
</span><span class="cx"> } // namespace WebCore
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesindexeddbserverSQLiteIDBBackingStoreh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h (209143 => 209144)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h        2016-11-30 19:48:04 UTC (rev 209143)
+++ trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h        2016-11-30 19:59:01 UTC (rev 209144)
</span><span class="lines">@@ -39,6 +39,7 @@
</span><span class="cx"> 
</span><span class="cx"> class IndexKey;
</span><span class="cx"> class SQLiteDatabase;
</span><ins>+class SQLiteStatement;
</ins><span class="cx"> 
</span><span class="cx"> namespace IDBServer {
</span><span class="cx"> 
</span><span class="lines">@@ -116,6 +117,56 @@
</span><span class="cx">     IDBError getAllObjectStoreRecords(const IDBResourceIdentifier&amp; transactionIdentifier, const IDBGetAllRecordsData&amp;, IDBGetAllResult&amp; outValue);
</span><span class="cx">     IDBError getAllIndexRecords(const IDBResourceIdentifier&amp; transactionIdentifier, const IDBGetAllRecordsData&amp;, IDBGetAllResult&amp; outValue);
</span><span class="cx"> 
</span><ins>+    void closeSQLiteDB();
+
+    enum class SQL : size_t {
+        CreateObjectStoreInfo,
+        CreateObjectStoreKeyGenerator,
+        DeleteObjectStoreInfo,
+        DeleteObjectStoreKeyGenerator,
+        DeleteObjectStoreRecords,
+        DeleteObjectStoreIndexInfo,
+        DeleteObjectStoreIndexRecords,
+        DeleteObjectStoreBlobRecords,
+        RenameObjectStore,
+        ClearObjectStoreRecords,
+        ClearObjectStoreIndexRecords,
+        CreateIndexInfo,
+        DeleteIndexInfo,
+        HasIndexRecord,
+        PutIndexRecord,
+        DeleteIndexRecords,
+        RenameIndex,
+        KeyExistsInObjectStore,
+        GetUnusedBlobFilenames,
+        DeleteUnusedBlobs,
+        GetObjectStoreRecordID,
+        DeleteBlobRecord,
+        DeleteObjectStoreRecord,
+        DeleteObjectStoreIndexRecord,
+        AddObjectStoreRecord,
+        AddBlobRecord,
+        BlobFilenameForBlobURL,
+        AddBlobFilename,
+        GetBlobURL,
+        GetKeyGeneratorValue,
+        SetKeyGeneratorValue,
+        GetAllKeyRecordsLowerOpenUpperOpen,
+        GetAllKeyRecordsLowerOpenUpperClosed,
+        GetAllKeyRecordsLowerClosedUpperOpen,
+        GetAllKeyRecordsLowerClosedUpperClosed,
+        GetValueRecordsLowerOpenUpperOpen,
+        GetValueRecordsLowerOpenUpperClosed,
+        GetValueRecordsLowerClosedUpperOpen,
+        GetValueRecordsLowerClosedUpperClosed,
+        Count
+    };
+
+    SQLiteStatement* cachedStatement(SQL, const char*);
+    SQLiteStatement* cachedStatementForGetAllObjectStoreRecords(const IDBGetAllRecordsData&amp;);
+
+    std::unique_ptr&lt;SQLiteStatement&gt; m_cachedStatements[static_cast&lt;int&gt;(SQL::Count)];
+
</ins><span class="cx">     JSC::VM&amp; vm();
</span><span class="cx">     JSC::JSGlobalObject&amp; globalObject();
</span><span class="cx">     void initializeVM();
</span></span></pre>
</div>
</div>

</body>
</html>