<!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" /><style type="text/css"><!--
#msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer { 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, #msg p { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; }
#msg ul { overflow: auto; }
#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>
<title>[28228] trunk/WebCore</title>
</head>
<body>

<div id="msg">
<dl>
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/28228">28228</a></dd>
<dt>Author</dt> <dd>beidson@apple.com</dd>
<dt>Date</dt> <dd>2007-11-29 23:01:25 -0800 (Thu, 29 Nov 2007)</dd>
</dl>

<h3>Log Message</h3>
<pre>        Reviewed by Tim

        Part of &lt;rdar://problem/5556377&gt; - Need to accurately track and enforce database quota

        SQLite has the ability to enforce the page usage for databases.  This gives us bulletproof
        enforcement of the database size limit with reasonable granularity (within 1023 bytes by 
        default on OS X with SQLite 3.4.0)

        This also involved enhancing the ability to install/remove the authorizer to run the PRAGMA
        statements involved

        Note this patch does not actually use the new maximumSize() functionality of the 
        SQLiteDatabase class - that can be reviewed/landed seperately
        
        * platform/sql/SQLiteDatabase.cpp:
        (WebCore::SQLiteDatabase::SQLiteDatabase): 
        (WebCore::SQLiteDatabase::maximumSize): Return the current maximum size in bytes
        (WebCore::SQLiteDatabase::setMaximumSize): Based on m_pageSize, set the maximum page count
          to enforce the maximum size in bytes
        (WebCore::SQLiteDatabase::pageSize): Fetch m_pageSize if it hasn't been fetched, or return
          the cached value
        (WebCore::SQLiteDatabase::setAuthorizer): Use enableAuthorizer for the sqlite_* level 
          authorizer setup
        (WebCore::SQLiteDatabase::enableAuthorizer): Install or remove the sqlite authorizer handler
        * platform/sql/SQLiteDatabase.h: Add the m_pageSize member so we only have to fetch it once</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkWebCoreChangeLog">trunk/WebCore/ChangeLog</a></li>
<li><a href="#trunkWebCoreplatformsqlSQLiteDatabasecpp">trunk/WebCore/platform/sql/SQLiteDatabase.cpp</a></li>
<li><a href="#trunkWebCoreplatformsqlSQLiteDatabaseh">trunk/WebCore/platform/sql/SQLiteDatabase.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/WebCore/ChangeLog (28227 => 28228)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/WebCore/ChangeLog        2007-11-30 05:50:15 UTC (rev 28227)
+++ trunk/WebCore/ChangeLog        2007-11-30 07:01:25 UTC (rev 28228)
</span><span class="lines">@@ -1,3 +1,31 @@
</span><ins>+2007-11-29  Brady Eidson &lt;beidson@apple.com&gt;
+
+        Reviewed by Tim
+
+        Part of &lt;rdar://problem/5556377&gt; - Need to accurately track and enforce database quota
+
+        SQLite has the ability to enforce the page usage for databases.  This gives us bulletproof
+        enforcement of the database size limit with reasonable granularity (within 1023 bytes by 
+        default on OS X with SQLite 3.4.0)
+
+        This also involved enhancing the ability to install/remove the authorizer to run the PRAGMA
+        statements involved
+
+        Note this patch does not actually use the new maximumSize() functionality of the 
+        SQLiteDatabase class - that can be reviewed/landed seperately
+        
+        * platform/sql/SQLiteDatabase.cpp:
+        (WebCore::SQLiteDatabase::SQLiteDatabase): 
+        (WebCore::SQLiteDatabase::maximumSize): Return the current maximum size in bytes
+        (WebCore::SQLiteDatabase::setMaximumSize): Based on m_pageSize, set the maximum page count
+          to enforce the maximum size in bytes
+        (WebCore::SQLiteDatabase::pageSize): Fetch m_pageSize if it hasn't been fetched, or return
+          the cached value
+        (WebCore::SQLiteDatabase::setAuthorizer): Use enableAuthorizer for the sqlite_* level 
+          authorizer setup
+        (WebCore::SQLiteDatabase::enableAuthorizer): Install or remove the sqlite authorizer handler
+        * platform/sql/SQLiteDatabase.h: Add the m_pageSize member so we only have to fetch it once
+
</ins><span class="cx"> 2007-11-29  Holger Hans Peter Freyther  &lt;holger.freyther@trolltech.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Reviewed by Maciej.
</span></span></pre></div>
<a id="trunkWebCoreplatformsqlSQLiteDatabasecpp"></a>
<div class="modfile"><h4>Modified: trunk/WebCore/platform/sql/SQLiteDatabase.cpp (28227 => 28228)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/WebCore/platform/sql/SQLiteDatabase.cpp        2007-11-30 05:50:15 UTC (rev 28227)
+++ trunk/WebCore/platform/sql/SQLiteDatabase.cpp        2007-11-30 07:01:25 UTC (rev 28228)
</span><span class="lines">@@ -44,6 +44,7 @@
</span><span class="cx"> 
</span><span class="cx"> SQLiteDatabase::SQLiteDatabase()
</span><span class="cx">     : m_db(0)
</span><ins>+    , m_pageSize(-1)
</ins><span class="cx">     , m_transactionInProgress(false)
</span><span class="cx">     , m_openingThread(0)
</span><span class="cx"> {
</span><span class="lines">@@ -98,6 +99,57 @@
</span><span class="cx">         executeCommand(&quot;PRAGMA fullfsync = 0;&quot;);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+int64_t SQLiteDatabase::maximumSize()
+{
+    MutexLocker locker(m_authorizerLock);
+    enableAuthorizer(false);
+    
+    SQLiteStatement statement(*this, &quot;PRAGMA max_page_count&quot;);
+    int64_t size = statement.getColumnInt64(0) * pageSize();
+    
+    enableAuthorizer(true);
+    return size;
+}
+
+void SQLiteDatabase::setMaximumSize(int64_t size)
+{
+    if (size &lt; 0)
+        size = 0;
+    
+    int currentPageSize = pageSize();
+
+    ASSERT(currentPageSize);
+    int64_t newMaxPageCount = currentPageSize ? size / currentPageSize : 0;
+    
+    MutexLocker locker(m_authorizerLock);
+    enableAuthorizer(false);
+
+    SQLiteStatement statement(*this, &quot;PRAGMA max_page_count = &quot; + String::number(newMaxPageCount));
+    statement.prepare();
+    if (statement.step() != SQLResultRow)
+        LOG_ERROR(&quot;Failed to set maximum size of database to %lli bytes&quot;, size);
+
+    enableAuthorizer(true);
+
+}
+
+int SQLiteDatabase::pageSize()
+{
+    // Since the page size of a database is locked in at creation and therefore cannot be dynamic, 
+    // we can cache the value for future use
+    if (m_pageSize == -1) {
+        MutexLocker locker(m_authorizerLock);
+        enableAuthorizer(false);
+        
+        SQLiteStatement statement(*this, &quot;PRAGMA page_size&quot;);
+        m_pageSize = statement.getColumnInt(0);
+        
+        enableAuthorizer(true);
+    }
+
+    return m_pageSize;
+}
+
</ins><span class="cx"> void SQLiteDatabase::setSynchronous(SynchronousPragma sync)
</span><span class="cx"> {
</span><span class="cx">     executeCommand(String::format(&quot;PRAGMA synchronous = %i&quot;, sync));
</span><span class="lines">@@ -275,7 +327,13 @@
</span><span class="cx">     MutexLocker locker(m_authorizerLock);
</span><span class="cx"> 
</span><span class="cx">     m_authorizer = auth;
</span><del>-    if (m_authorizer)
</del><ins>+    
+    enableAuthorizer(true);
+}
+
+void SQLiteDatabase::enableAuthorizer(bool enable)
+{
+    if (m_authorizer &amp;&amp; enable)
</ins><span class="cx">         sqlite3_set_authorizer(m_db, SQLiteDatabase::authorizerFunction, m_authorizer.get());
</span><span class="cx">     else
</span><span class="cx">         sqlite3_set_authorizer(m_db, NULL, 0);
</span></span></pre></div>
<a id="trunkWebCoreplatformsqlSQLiteDatabaseh"></a>
<div class="modfile"><h4>Modified: trunk/WebCore/platform/sql/SQLiteDatabase.h (28227 => 28228)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/WebCore/platform/sql/SQLiteDatabase.h        2007-11-30 05:50:15 UTC (rev 28227)
+++ trunk/WebCore/platform/sql/SQLiteDatabase.h        2007-11-30 07:01:25 UTC (rev 28228)
</span><span class="lines">@@ -81,6 +81,13 @@
</span><span class="cx">     // TODO - add pragma and sqlite_master accessors here
</span><span class="cx">     void setFullsync(bool);
</span><span class="cx">     
</span><ins>+    // Gets/sets the maximum size in bytes
+    // Depending on per-database attributes, the size will only be settable in units that are the page size of the database, which is established at creation
+    // These chunks will never be anything other than 512, 1024, 2048, 4096, 8192, 16384, or 32768 bytes in size.
+    // setMaximumSize() will round the size down to the next smallest chunk if the passed size doesn't align.
+    int64_t maximumSize();
+    void setMaximumSize(int64_t);
+    
</ins><span class="cx">     // The SQLite SYNCHRONOUS pragma can be either FULL, NORMAL, or OFF
</span><span class="cx">     // FULL - Any writing calls to the DB block until the data is actually on the disk surface
</span><span class="cx">     // NORMAL - SQLite pauses at some critical moments when writing, but much less than FULL
</span><span class="lines">@@ -106,9 +113,14 @@
</span><span class="cx"> private:
</span><span class="cx">     static int authorizerFunction(void*, int, const char*, const char*, const char*, const char*);
</span><span class="cx"> 
</span><ins>+    void enableAuthorizer(bool enable);
+    
+    int pageSize();
+    
</ins><span class="cx">     String   m_path;
</span><span class="cx">     sqlite3* m_db;
</span><span class="cx">     int m_lastError;
</span><ins>+    int m_pageSize;
</ins><span class="cx">     
</span><span class="cx">     bool m_transactionInProgress;
</span><span class="cx">     
</span></span></pre>
</div>
</div>

</body>
</html>