diff options
Diffstat (limited to '')
-rw-r--r-- | www/rbu.html | 765 |
1 files changed, 765 insertions, 0 deletions
diff --git a/www/rbu.html b/www/rbu.html new file mode 100644 index 0000000..c422466 --- /dev/null +++ b/www/rbu.html @@ -0,0 +1,765 @@ +<!DOCTYPE html> +<html><head> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta http-equiv="content-type" content="text/html; charset=UTF-8"> +<link href="sqlite.css" rel="stylesheet"> +<title>The RBU Extension</title> +<!-- path= --> +</head> +<body> +<div class=nosearch> +<a href="index.html"> +<img class="logo" src="images/sqlite370_banner.gif" alt="SQLite" border="0"> +</a> +<div><!-- IE hack to prevent disappearing logo --></div> +<div class="tagline desktoponly"> +Small. Fast. Reliable.<br>Choose any three. +</div> +<div class="menu mainmenu"> +<ul> +<li><a href="index.html">Home</a> +<li class='mobileonly'><a href="javascript:void(0)" onclick='toggle_div("submenu")'>Menu</a> +<li class='wideonly'><a href='about.html'>About</a> +<li class='desktoponly'><a href="docs.html">Documentation</a> +<li class='desktoponly'><a href="download.html">Download</a> +<li class='wideonly'><a href='copyright.html'>License</a> +<li class='desktoponly'><a href="support.html">Support</a> +<li class='desktoponly'><a href="prosupport.html">Purchase</a> +<li class='search' id='search_menubutton'> +<a href="javascript:void(0)" onclick='toggle_search()'>Search</a> +</ul> +</div> +<div class="menu submenu" id="submenu"> +<ul> +<li><a href='about.html'>About</a> +<li><a href='docs.html'>Documentation</a> +<li><a href='download.html'>Download</a> +<li><a href='support.html'>Support</a> +<li><a href='prosupport.html'>Purchase</a> +</ul> +</div> +<div class="searchmenu" id="searchmenu"> +<form method="GET" action="search"> +<select name="s" id="searchtype"> +<option value="d">Search Documentation</option> +<option value="c">Search Changelog</option> +</select> +<input type="text" name="q" id="searchbox" value=""> +<input type="submit" value="Go"> +</form> +</div> +</div> +<script> +function toggle_div(nm) { +var w = document.getElementById(nm); +if( w.style.display=="block" ){ +w.style.display = "none"; +}else{ +w.style.display = "block"; +} +} +function toggle_search() { +var w = document.getElementById("searchmenu"); +if( w.style.display=="block" ){ +w.style.display = "none"; +} else { +w.style.display = "block"; +setTimeout(function(){ +document.getElementById("searchbox").focus() +}, 30); +} +} +function div_off(nm){document.getElementById(nm).style.display="none";} +window.onbeforeunload = function(e){div_off("submenu");} +/* Disable the Search feature if we are not operating from CGI, since */ +/* Search is accomplished using CGI and will not work without it. */ +if( !location.origin || !location.origin.match || !location.origin.match(/http/) ){ +document.getElementById("search_menubutton").style.display = "none"; +} +/* Used by the Hide/Show button beside syntax diagrams, to toggle the */ +function hideorshow(btn,obj){ +var x = document.getElementById(obj); +var b = document.getElementById(btn); +if( x.style.display!='none' ){ +x.style.display = 'none'; +b.innerHTML='show'; +}else{ +x.style.display = ''; +b.innerHTML='hide'; +} +return false; +} +var antiRobot = 0; +function antiRobotGo(){ +if( antiRobot!=3 ) return; +antiRobot = 7; +var j = document.getElementById("mtimelink"); +if(j && j.hasAttribute("data-href")) j.href=j.getAttribute("data-href"); +} +function antiRobotDefense(){ +document.body.onmousedown=function(){ +antiRobot |= 2; +antiRobotGo(); +document.body.onmousedown=null; +} +document.body.onmousemove=function(){ +antiRobot |= 2; +antiRobotGo(); +document.body.onmousemove=null; +} +setTimeout(function(){ +antiRobot |= 1; +antiRobotGo(); +}, 100) +antiRobotGo(); +} +antiRobotDefense(); +</script> +<div class=fancy> +<div class=nosearch> +<div class="fancy_title"> +The RBU Extension +</div> +<div class="fancy_toc"> +<a onclick="toggle_toc()"> +<span class="fancy_toc_mark" id="toc_mk">►</span> +Table Of Contents +</a> +<div id="toc_sub"><div class="fancy-toc1"><a href="#the_rbu_extension">1. The RBU Extension</a></div> +<div class="fancy-toc1"><a href="#rbu_updates">2. RBU Updates</a></div> +<div class="fancy-toc2"><a href="#rbu_update_limitations">2.1. RBU Update Limitations</a></div> +<div class="fancy-toc2"><a href="#preparing_an_rbu_update_file">2.2. Preparing an RBU Update File</a></div> +<div class="fancy-toc3"><a href="#the_rbu_database_schema">2.2.1. The RBU Database Schema</a></div> +<div class="fancy-toc3"><a href="#rbu_database_contents">2.2.2. RBU Database Contents</a></div> +<div class="fancy-toc3"><a href="#using_rbu_with_fts3_4_tables">2.2.3. Using RBU with FTS3/4 Tables</a></div> +<div class="fancy-toc3"><a href="#automatically_generating_rbu_updates_with_sqldiff">2.2.4. Automatically Generating RBU Updates with sqldiff</a></div> +<div class="fancy-toc2"><a href="#rbu_update_c_c_programming">2.3. RBU Update C/C++ Programming</a></div> +<div class="fancy-toc1"><a href="#rbu_vacuum">3. RBU Vacuum</a></div> +<div class="fancy-toc2"><a href="#rbu_vacuum_limitations">3.1. RBU Vacuum Limitations</a></div> +<div class="fancy-toc2"><a href="#rbu_vacuum_c_c_programming">3.2. RBU Vacuum C/C++ Programming</a></div> +</div> +</div> +<script> +function toggle_toc(){ +var sub = document.getElementById("toc_sub") +var mk = document.getElementById("toc_mk") +if( sub.style.display!="block" ){ +sub.style.display = "block"; +mk.innerHTML = "▼"; +} else { +sub.style.display = "none"; +mk.innerHTML = "►"; +} +} +</script> +</div> + + + +<h1 align="center" id="the_rbu_extension"><span>1. </span>The RBU Extension</h1> + +<p>The RBU extension is an add-on for SQLite designed for use with large +SQLite database files on low-power devices at the edge of a network. RBU +may be used for two separate tasks: + +</p><ul> + <li> <b>RBU Update operations</b>. An <a href="rbu.html#rbu_updates">RBU Update</a> is a bulk update of a + database file that may include many insert, update and delete + operations on one or more tables. + </li><li> <b>RBU Vacuum operations</b>. An <a href="rbu.html#rbu_vacuum">RBU Vacuum</a> optimizes and rebuilds an + entire database file, with results similar to SQLite's native VACUUM + command. +</li></ul> + +<p>The acronym RBU stands for "Resumable Bulk Update". + +</p><p>Both of the RBU functions may be accomplished using SQLite's built-in +SQL commands - RBU update via a series of <a href="lang_insert.html">INSERT</a>, <a href="lang_delete.html">DELETE</a> and +<a href="lang_update.html">UPDATE</a> commands within a single transaction, and RBU vacuum by a single +<a href="lang_vacuum.html">VACUUM</a> command. The RBU module provides the following advantages over +these simpler approaches: + + +</p><ol> +<li><b>RBU may be more efficient</b> + +<p>The most efficient way to apply changes to a B-Tree (the data structure +that SQLite uses to store each table and index on disk) is to make the +changes in key order. But if an SQL table has one or more indexes, the key +order for each index may be different from the main table and the other +auxiliary indexes. As a result, when executing a series of <a href="lang_insert.html">INSERT</a>, +<a href="lang_update.html">UPDATE</a> and <a href="lang_delete.html">DELETE</a> statements it is not generally possible to order the +operations so that all b-trees are updated in key order. The RBU update +process works around this by applying all changes to the main table in one +pass, then applying changes to each index in separate passes, ensuring each +B-Tree is updated optimally. For a large database file (one that does not +fit in the OS disk cache) this procedure can result in two orders of +magnitude faster updates. + +</p><p>An RBU Vacuum operation requires less temporary disk space and writes +less data to disk than an SQLite VACUUM. An SQLite VACUUM requires roughly +twice the size of the final database file in temporary disk space to run. +The total amount of data written is around three times the size of the +final database file. By contrast, an RBU Vacuum requires roughly the size +of the final database file in temporary disk space and writes a total of +twice that to disk. + +</p><p>On the other hand, an RBU Vacuum uses more CPU than a regular SQLite +VACUUM - in one test as much as five times as much. For this reason, an RBU +Vacuum is often significantly slower than an SQLite VACUUM under the same +conditions. + +</p></li><li><b>RBU runs in the background</b> + +<p>An ongoing RBU operation (either an update or a vacuum) does not +interfere with read access to the database file. + +</p></li><li><b>RBU runs incrementally</b> + +<p>RBU operations may be suspended and then later resumed, perhaps with +intervening power outages and/or system resets. For an RBU update, the +original database content remains visible to all database readers until +the entire update has been applied - even if the update is suspended and +then later resumed. + +</p></li></ol> + +<p>The RBU extension is not enabled by default. To enable it, compile the +<a href="amalgamation.html">amalgamation</a> with the <a href="compile.html#enable_rbu">SQLITE_ENABLE_RBU</a> compile-time option. + +<a name="rbu_updates"></a> + +</p><h1 id="rbu_updates"><span>2. </span>RBU Updates</h1> +<h2 id="rbu_update_limitations"><span>2.1. </span>RBU Update Limitations</h2> + +<p>The following limitations apply to RBU updates: + +</p><ul> +<li><p>The changes must consist of <a href="lang_insert.html">INSERT</a>, <a href="lang_update.html">UPDATE</a>, and <a href="lang_delete.html">DELETE</a> + operations only. CREATE and DROP operations are not + supported.</p></li> +<li><p><a href="lang_insert.html">INSERT</a> statements may not use default values.</p></li> +<li><p><a href="lang_update.html">UPDATE</a> and <a href="lang_delete.html">DELETE</a> statements must identify the target rows + by rowid or by non-NULL PRIMARY KEY values.</p></li> +<li><p><a href="lang_update.html">UPDATE</a> statements may not modify PRIMARY KEY or rowid values. + </p></li> +<li><p>RBU updates cannot be applied to any tables that contain a column + named "rbu_control".</p></li> +<li><p>The RBU update will not fire any triggers.</p></li> +<li><p>The RBU update will not detect or prevent foreign key or + CHECK constraint violations.</p></li> +<li><p>All RBU updates use the "OR ROLLBACK" constraint handling mechanism. + </p></li> +<li><p>The target database may not be in <a href="wal.html">WAL mode</a>.</p></li> +<li><p></p><s>The target database may not contain <a href="expridx.html">indexes on expressions</a>.</s> + Indexes on expressions are supported beginning with SQLite 3.30.0 + (2019-10-04). +</li><li><p>No other writes may occur on the target database while the + RBU update is being applied. A read-lock is held on the target + database to prevent this.</p></li> +</ul> + + +<h2 id="preparing_an_rbu_update_file"><span>2.2. </span>Preparing an RBU Update File</h2> + +<p>All changes to be applied by RBU are stored in a separate SQLite database +called the "RBU database". The database that is to be modified is called +the "target database". + +</p><p>For each table in the target database that will be modified by the update, +a corresponding table is created within the RBU database. The RBU database +table schema is not the same as that of the target database, but is derived +from it as <a href="rbu.html#database_tables">described below</a>. + +</p><p>The RBU database table contains a single row for each target database +row inserted, updated or deleted by the update. Populating the RBU database +tables is described in <a href="rbu.html#database_contents">the following section</a>. +</p> + +<a name="database_tables"></a> + +<h3 id="the_rbu_database_schema"><span>2.2.1. </span>The RBU Database Schema</h3> + +<p> +For each table in the target database, the RBU database should contain a table +named "data<<i>integer</i>>_<<i>target-table-name</i>>" where +<<i>target-table-name</i>> is the name of the table in the target +database and <<i>integer</i>> is any sequence of zero or more numeric +characters (0-9). Tables within the RBU database are processed in order by +name (from smallest to largest according to the BINARY collation sequence), +so the order in which target tables are updated is influenced by the selection +of the <<i>integer</i>> portion of the data_% table name. While this can +be useful when using RBU to update +<a href="rbu.html#fts4_tables">certain types of virtual tables</a>, there is normally no +reason to use anything other than an empty string in place of +<<i>integer</i>>. + +</p><p>The data_% table must have all the same columns as the target table, plus +one additional column named "rbu_control". The data_% table should have no +PRIMARY KEY or UNIQUE constraints, but each column should have the same type as +the corresponding column in the target database. The rbu_control column should +have no type at all. For example, if the target database contains: + +</p><div class="codeblock"><pre>CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c UNIQUE); +</pre></div> + +<p>Then the RBU database should contain: + +</p><div class="codeblock"><pre>CREATE TABLE data_t1(a INTEGER, b TEXT, c, rbu_control); +</pre></div> + +<p>The order of the columns in the data_% table does not matter. + +</p><p>If the target database table is a virtual table or a table that has no +PRIMARY KEY declaration, the data_% table must also contain a column +named "rbu_rowid". The rbu_rowid column is mapped to the tables <a href="lang_createtable.html#rowid">ROWID</a>. +For example, if the target database contains either of the following: + +</p><div class="codeblock"><pre>CREATE VIRTUAL TABLE x1 USING fts3(a, b); +CREATE TABLE x1(a, b); +</pre></div> + +<p>then the RBU database should contain: + +</p><div class="codeblock"><pre>CREATE TABLE data_x1(a, b, rbu_rowid, rbu_control); +</pre></div> + +<p>Virtual tables for which the "rowid" column does +not function like a primary key value cannot be updated using RBU. + +</p><p> +All non-hidden columns (i.e. all columns matched by "SELECT *") of the +target table must be present in the input table. For virtual tables, +hidden columns are optional - they are updated by RBU if present in +the input table, or not otherwise. For example, to write to an fts4 +table with a hidden languageid column such as: + +</p><div class="codeblock"><pre>CREATE VIRTUAL TABLE ft1 USING fts4(a, b, languageid='langid'); +</pre></div> + +<p>Either of the following input table schemas may be used: + +</p><div class="codeblock"><pre>CREATE TABLE data_ft1(a, b, langid, rbu_rowid, rbu_control); +CREATE TABLE data_ft1(a, b, rbu_rowid, rbu_control); +</pre></div> + +<a name="database_contents"></a> + +<h3 id="rbu_database_contents"><span>2.2.2. </span>RBU Database Contents</h3> + +<p>For each row to INSERT into the target database as part of the RBU +update, the corresponding data_% table should contain a single record +with the "rbu_control" column set to contain integer value 0. The +other columns should be set to the values that make up the new record +to insert. + +</p><p>The "rbu_control" column may also be set to integer value 2 for +an INSERT. In this case, the new row silently replaces any existing row that +has the same primary key values. This is equivalent to a DELETE followed by an +INSERT with the same primary key values. It is not the same as an SQL REPLACE +command, as in that case the new row may replace any conflicting rows (i.e. +those that conflict due to UNIQUE constraints or indexes), not just those with +conflicting primary keys. + +</p><p>If the target database table has an INTEGER PRIMARY KEY, it is not +possible to insert a NULL value into the IPK column. Attempting to +do so results in an SQLITE_MISMATCH error. + +</p><p>For each row to DELETE from the target database as part of the RBU +update, the corresponding data_% table should contain a single record +with the "rbu_control" column set to contain integer value 1. The +real primary key values of the row to delete should be stored in the +corresponding columns of the data_% table. The values stored in the +other columns are not used. + +</p><p>For each row to UPDATE from the target database as part of the RBU +update, the corresponding data_% table should contain a single record +with the "rbu_control" column set to contain a value of type text. +The real primary key values identifying the row to update should be +stored in the corresponding columns of the data_% table row, as should +the new values of all columns being update. The text value in the +"rbu_control" column must contain the same number of characters as +there are columns in the target database table, and must consist entirely +of 'x' and '.' characters (or in some special cases 'd' - see below). For +each column that is being updated, the corresponding character is set to +'x'. For those that remain as they are, the corresponding character of the +rbu_control value should be set to '.'. For example, given the tables +above, the update statement: + +</p><div class="codeblock"><pre>UPDATE t1 SET c = 'usa' WHERE a = 4; +</pre></div> + +<p>is represented by the data_t1 row created by: + +</p><div class="codeblock"><pre>INSERT INTO data_t1(a, b, c, rbu_control) VALUES(4, NULL, 'usa', '..x'); +</pre></div> + +<p>If RBU is used to update a large BLOB value within a target database, it +may be more efficient to store a patch or delta that can be used to modify +the existing BLOB instead of an entirely new value within the RBU database. +RBU allows deltas to be specified in two ways: + +</p><ul> + <li> In the "fossil delta" format - the format used for blob deltas by the + <a href="http://fossil-scm.org">Fossil source-code management system</a>, or + + </li><li> In a custom format defined by the RBU application. +</li></ul> + +<p> The fossil delta format may only be used to update BLOB values. Instead +of storing the new BLOB within the data_% table, the fossil delta is stored +instead. And instead of specifying an 'x' as part of the rbu_control string +for the column to be updated, an 'f' character is stored. When processing +an 'f' update, RBU loads the original BLOB data from disk, applies the fossil +delta to it and stores the results back into the database file. The RBU +databases generated by <a href="rbu.html#sqldiff">sqldiff --rbu</a> make use of fossil deltas wherever +doing so would save space in the RBU database. + +</p><p> To use a custom delta format, the RBU application must register a +user-defined SQL function named "rbu_delta" before beginning to process the +update. rbu_delta() will be invoked with two arguments - the original value +stored in the target table column and the delta value provided as part of +the RBU update. It should return the result of applying the delta to the +original value. To use the custom delta function, the character of the +rbu_control value corresponding to the target column to update must be +set to 'd' instead of 'x'. Then, instead of updating the target table with the +value stored in the corresponding data_% column, RBU invokes the user-defined +SQL function "rbu_delta()" and the store in the target table column. + +</p><p>For example, this row: + +</p><div class="codeblock"><pre>INSERT INTO data_t1(a, b, c, rbu_control) VALUES(4, NULL, 'usa', '..d'); +</pre></div> + +<p>causes RBU to update the target database table in a way similar to: + +</p><div class="codeblock"><pre>UPDATE t1 SET c = rbu_delta(c, 'usa') WHERE a = 4; +</pre></div> + +<p>If the target database table is a virtual table or a table with no PRIMARY +KEY, the rbu_control value should not include a character corresponding +to the rbu_rowid value. For example, this: + +</p><div class="codeblock"><pre>INSERT INTO data_ft1(a, b, rbu_rowid, rbu_control) + VALUES(NULL, 'usa', 12, '.x'); +</pre></div> + + +<p>causes a result similar to: + +</p><div class="codeblock"><pre>UPDATE ft1 SET b = 'usa' WHERE rowid = 12; +</pre></div> + +<p>The data_% tables themselves should have no PRIMARY KEY declarations. +However, RBU is more efficient if reading the rows in from each data_% +table in "rowid" order is roughly the same as reading them sorted by +the PRIMARY KEY of the corresponding target database table. In other +words, rows should be sorted using the destination table PRIMARY KEY +fields before they are inserted into the data_% tables. + +<a name="fts4_tables"></a> + +</p><h3 id="using_rbu_with_fts3_4_tables"><span>2.2.3. </span>Using RBU with FTS3/4 Tables</h3> + +<p>Usually, an <a href="fts3.html">FTS3 or FTS4</a> table is an example of a virtual table +with a rowid that works like a PRIMARY KEY. So, for the following FTS4 tables: + +</p><div class="codeblock"><pre>CREATE VIRTUAL TABLE ft1 USING fts4(addr, text); +CREATE VIRTUAL TABLE ft2 USING fts4; -- implicit "content" column +</pre></div> + +<p>The data_% tables may be created as follows: + +</p><div class="codeblock"><pre>CREATE TABLE data_ft1 USING fts4(addr, text, rbu_rowid, rbu_control); +CREATE TABLE data_ft2 USING fts4(content, rbu_rowid, rbu_control); +</pre></div> + +<p>And populated as if the target table were an ordinary SQLite table with no +explicit PRIMARY KEY columns. + +</p><p><a href="fts3.html#_contentless_fts4_tables_">Contentless FTS4 tables</a> are handled similarly, +except that any attempt to update or delete rows will cause an error when +applying the update. + +</p><p><a href="fts3.html#_external_content_fts4_tables_">External content FTS4 tables</a> may also be +updated using RBU. In this case the user is required to configure the RBU +database so that the same set of UPDATE, DELETE and INSERT operations are +applied to the FTS4 index as to the underlying content table. As for all +updates of external content FTS4 tables, the user is also required to ensure +that any UPDATE or DELETE operations are applied to the FTS4 index before +they are applied to the underlying content table (refer to FTS4 documentation +for a detailed explanation). In RBU, this is done by ensuring that the name +of the data_% table used to write to the FTS4 table sorts before the name +of the data_% table used to update the underlying content table using the +<a href="datatype3.html#collation">BINARY</a> collation sequence. In order to avoid duplicating data within the +RBU database, an SQL view may be used in place of one of the data_% tables. +For example, for the target database schema: + +</p><div class="codeblock"><pre>CREATE TABLE ccc(addr, text); +CREATE VIRTUAL TABLE ccc_fts USING fts4(addr, text, content=ccc); +</pre></div> + +<p> + The following RBU database schema may be used: + +</p><div class="codeblock"><pre>CREATE TABLE data_ccc(addr, text, rbu_rowid, rbu_control); +CREATE VIEW data0_ccc_fts AS SELECT * FROM data_ccc; +</pre></div> + +<p> + The data_ccc table may then be populated as normal with the updates intended + for target database table ccc. The same updates will be read by RBU from + the data0_ccc_fts view and applied to FTS table ccc_fts. Because + "data0_ccc_fts" is smaller than "data_ccc", the FTS table will be updated + first, as required. + +</p><p> + Cases in which the underlying content table has an explicit INTEGER PRIMARY + KEY column are slightly more difficult, as the text values stored in the + rbu_control column are slightly different for the FTS index and its + underlying content table. For the underlying content table, a character + must be included in any rbu_control text values for the explicit IPK, but + for the FTS table itself, which has an implicit rowid, it should not. This + is inconvenient, but can be solved using a more complicated view, as follows: + +</p><div class="codeblock"><pre>-- Target database schema +CREATE TABLE ddd(i INTEGER PRIMARY KEY, k TEXT); +CREATE VIRTUAL TABLE ddd_fts USING fts4(k, content=ddd); + +-- RBU database schema +CREATE TABLE data_ccc(i, k, rbu_control); +CREATE VIEW data0_ccc_fts AS SELECT i AS rbu_rowid, k, CASE + WHEN rbu_control IN (0,1) THEN rbu_control ELSE substr(rbu_control, 2) END +FROM data_ccc; +</pre></div> + +<p> + The substr() function in the SQL view above returns the text of the + rbu_control argument with the first character (the one corresponding to + column "i", which is not required by the FTS table) removed. + +<a name="sqldiff"></a> + +</p><h3 id="automatically_generating_rbu_updates_with_sqldiff"><span>2.2.4. </span>Automatically Generating RBU Updates with sqldiff</h3> + +<p> + As of SQLite <a href="releaselog/3_9_0.html">version 3.9.0</a> (2015-10-14), + the <a href="sqldiff.html">sqldiff</a> utility is able to generate + RBU databases representing the difference between two databases with + identical schemas. For example, the following command: + +</p><div class="codeblock"><pre>sqldiff --rbu t1.db t2.db +</pre></div> + +<p> + Outputs an SQL script to create an RBU database which, if used to update + database t1.db, patches it so that its contents are identical to that of + database t2.db. + +</p><p> + By default, sqldiff attempts to process all non-virtual tables within + the two databases provided to it. If any table appears in one database + but not the other, or if any table has a slightly different schema in + one database it is an error. The "--table" option may be useful if this + causes a problem + +</p><p> + Virtual tables are ignored by default by sqldiff. However, it is possible + to explicitly create an RBU data_% table for a virtual table that features + a rowid that functions like a primary key using a command such as: + +</p><div class="codeblock"><pre>sqldiff --rbu --table <<i>virtual-table-name</i>> t1.db t2.db +</pre></div> + +<p> + Unfortunately, even though virtual tables are ignored by default, any + <a href="fts3.html#*shadowtab">underlying database tables</a> that they create in order to + store data within the database are not, and <a href="sqldiff.html">sqldiff</a> will include add these + to any RBU database. For this reason, users attempting to use sqldiff to + create RBU updates to apply to target databases with one or more virtual + tables will likely have to run sqldiff using the --table option separately + for each table to update in the target database. + +</p><h2 id="rbu_update_c_c_programming"><span>2.3. </span>RBU Update C/C++ Programming</h2> + +<p>The RBU extension interface allows an application to apply an RBU update +stored in an RBU database to an existing target database. +The procedure is as follows: + +</p><ol> +<li><p> +Open an RBU handle using the sqlite3rbu_open(T,A,S) function. + +</p><p>The T argument is the name of the target database file. +The A argument is the name of the RBU database file. +The S argument is the name of a "state database" used to store +state information needed to resume the update after an interruption. +The S argument can be NULL in which case the state information +is stored in the RBU database in various tables whose names all +begin with "rbu_". + +</p><p>The sqlite3rbu_open(T,A,S) function returns a pointer to +an "sqlite3rbu" object, which is then passed into the subsequent +interfaces. + + +</p></li><li><p> +Register any required virtual table modules with the database +handle returned by sqlite3rbu_db(X) (where argument X is the sqlite3rbu +pointer returned from sqlite3rbu_open()). Also, if required, register +the rbu_delta() SQL function using +<a href="c3ref/create_function.html">sqlite3_create_function_v2()</a>. + +</p></li><li><p> +Invoke the sqlite3rbu_step(X) function one or more times on +the sqlite3rbu object pointer X. Each call to sqlite3rbu_step() +performs a single b-tree operation, so thousands of calls may be +required to apply a complete update. The sqlite3rbu_step() +interface will return SQLITE_DONE when the update has been +completely applied. + +</p></li><li><p> +Call sqlite3rbu_close(X) to destroy the sqlite3rbu object pointer. +If sqlite3rbu_step(X) has been called enough times to completely +apply the update to the target database, then the RBU database +is marked as fully applied. Otherwise, the state of the RBU +update application is saved in the state database (or in the RBU +database if the name of the state database file in sqlite3rbu_open() +is NULL) for later resumption of the update. +</p></li></ol> + +<p>If an update is only partially applied to the target database by the +time sqlite3rbu_close() is called, state information is saved +within the state database if it exists, or otherwise in the RBU database. +This allows subsequent processes to automatically +resume the RBU update from where it left off. +If state information is stored in the RBU database, it can be removed +by dropping all tables whose names begin with "rbu_". + +</p><p>For more details, refer to the comments in +<a href="http://sqlite.org/src/doc/trunk/ext/rbu/sqlite3rbu.h">header file +sqlite3rbu.h</a>. + +<a name="rbu_vacuum"></a> + +</p><h1 id="rbu_vacuum"><span>3. </span>RBU Vacuum</h1> + +<h2 id="rbu_vacuum_limitations"><span>3.1. </span>RBU Vacuum Limitations</h2> + +<p>When compared with SQLite's built-in VACUUM command, RBU Vacuum has the +following limitations: + +</p><ul> + <li><p>It may not be used on a database that contains <a href="expridx.html">indexes on expressions</a>. + </p></li><li><p>The database being vacuumed may not be in <a href="wal.html">WAL mode</a>. +</p></li></ul> + +<h2 id="rbu_vacuum_c_c_programming"><span>3.2. </span>RBU Vacuum C/C++ Programming</h2> + +<p> This section provides an overview of and example code demonstrating the + integration of RBU Vacuum into an application program. For full details, + refer to the comments in + <a href="http://sqlite.org/src/doc/trunk/ext/rbu/sqlite3rbu.h">header file + sqlite3rbu.h</a>. + +</p><p> RBU Vacuum applications all implement some variation of the following +procedure: + +</p><ol> + <li><p> An RBU handle is created by calling sqlite3rbu_vacuum(T, S). + + </p><p> Argument T is the name of the database file to vacuum. Argument S is + the name of a database in which the RBU module will save its state if the + vacuum operation is suspended. + + </p><p> If state database S does not exist when sqlite3rbu_vacuum() is + invoked, it is automatically created and populated with the single table + used to store the state of an RBU vacuum - "rbu_state". If an ongoing RBU + vacuum is suspended, this table is populated with state data. The next + time sqlite3rbu_vacuum() is called with the same S parameter, it detects + this data and attempts to resume the suspended vacuum operation. When + an RBU vacuum operation is completed or encounters an error, RBU + automatically deletes the contents of the rbu_state table. In this case, + the next call to sqlite3rbu_vacuum() starts an entirely new vacuum + operation from scratch. + + </p><p> It is a good idea to establish a convention for determining the RBU + vacuum state database name based on the target database name. The + example code below uses "<target>-vacuum", where <target> is + the name of the database being vacuumed. + + </p></li><li><p> Any custom collation sequences used by indexes within the database + being vacuumed are registered with both of the database handles returned + by the sqlite3rbu_db() function. + + </p></li><li><p> Function sqlite3rbu_step() is called on the RBU handle until either + the RBU vacuum is finished, an error occurs or the application wishes to + suspend the RBU vacuum. + + </p><p> Each call to sqlite3rbu_step() does a small amount of work towards + completing the vacuum operation. Depending on the size of the database, a + single vacuum may require thousands of calls to sqlite3rbu_step(). + sqlite3rbu_step() returns SQLITE_DONE if the vacuum operation has + finished, SQLITE_OK if the vacuum operation has not finished but no error + has occurred, and an SQLite error code if an error is encountered. If + an error does occur, all subsequent calls to sqlite3rbu_step() immediately + return the same error code. + + </p></li><li><p> Finally, sqlite3rbu_close() is called to close the RBU handle. If the + application stopped calling sqlite3rbu_step() before either the vacuum + finished or an error occurred, the state of the vacuum is saved in the + state database so that it may be resumed later on. + + </p><p> Like sqlite3rbu_step(), if the vacuum operation has finished, + sqlite3rbu_close() returns SQLITE_DONE. If the vacuum has not finished + but no error has occurred, SQLITE_OK is returned. Or, if an error has + occurred, an SQLite error code is returned. If an error occurred as part + of a prior call to sqlite3rbu_step(), sqlite3rbu_close() returns the + same error code. +</p></li></ol> + +<p>The following example code illustrates the techniques described above. + +</p><div class="codeblock"><pre><i>/*</i> +<i>** Either start a new RBU vacuum or resume a suspended RBU vacuum on </i> +<i>** database zTarget. Return when either an error occurs, the RBU </i> +<i>** vacuum is finished or when the application signals an interrupt</i> +<i>** (code not shown).</i> +<i>**</i> +<i>** If the RBU vacuum is completed successfully, return SQLITE_DONE.</i> +<i>** If an error occurs, return SQLite error code. Or, if the application</i> +<i>** signals an interrupt, suspend the RBU vacuum operation so that it</i> +<i>** may be resumed by a subsequent call to this function and return</i> +<i>** SQLITE_OK.</i> +<i>**</i> +<i>** This function uses the database named "<zTarget>-vacuum" for</i> +<i>** the state database, where <zTarget> is the name of the database </i> +<i>** being vacuumed.</i> +<i>*/</i> +int do_rbu_vacuum(const char *zTarget){ + int rc; + char *zState; <i>/* Name of state database */</i> + sqlite3rbu *pRbu; <i>/* RBU vacuum handle */</i> + + zState = sqlite3_mprintf("%s-vacuum", zTarget); + if( zState==0 ) return SQLITE_NOMEM; + pRbu = sqlite3rbu_vacuum(zTarget, zState); + sqlite3_free(zState); + + if( pRbu ){ + sqlite3 *dbTarget = sqlite3rbu_db(pRbu, 0); + sqlite3 *dbState = sqlite3rbu_db(pRbu, 1); + + <i>/* Any custom collation sequences used by the target database must</i> + <i>** be registered with both database handles here. */</i> + + while( sqlite3rbu_step(pRbu)==SQLITE_OK ){ + if( <i><application has signaled interrupt></i> ) break; + } + } + rc = sqlite3rbu_close(pRbu); + return rc; +} +</pre></div> +<p align="center"><small><i>This page last modified on <a href="https://sqlite.org/docsrc/honeypot" id="mtimelink" data-href="https://sqlite.org/docsrc/finfo/pages/rbu.in?m=0f520f8581888500a">2019-09-26 19:34:22</a> UTC </small></i></p> + |