625 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			625 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
<!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 Session 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;
 | 
						|
}
 | 
						|
</script>
 | 
						|
</div>
 | 
						|
<div class=fancy>
 | 
						|
<div class=nosearch>
 | 
						|
<div class="fancy_title">
 | 
						|
The Session 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="#introduction">1. Introduction</a></div>
 | 
						|
<div class="fancy-toc2"><a href="#typical_use_case">1.1. Typical Use Case</a></div>
 | 
						|
<div class="fancy-toc2"><a href="#obtaining_the_session_extension">1.2. Obtaining the Session Extension</a></div>
 | 
						|
<div class="fancy-toc2"><a href="#limitations">1.3. Limitations</a></div>
 | 
						|
<div class="fancy-toc1"><a href="#concepts">2. Concepts</a></div>
 | 
						|
<div class="fancy-toc2"><a href="#changesets_and_patchsets">2.1. Changesets and Patchsets</a></div>
 | 
						|
<div class="fancy-toc2"><a href="#conflicts">2.2. Conflicts</a></div>
 | 
						|
<div class="fancy-toc2"><a href="#changeset_construction">2.3. Changeset Construction</a></div>
 | 
						|
<div class="fancy-toc1"><a href="#using_the_session_extension">3. Using The Session Extension</a></div>
 | 
						|
<div class="fancy-toc2"><a href="#capturing_a_changeset">3.1. Capturing a Changeset</a></div>
 | 
						|
<div class="fancy-toc2"><a href="#applying_a_changeset_to_a_database">3.2. Applying a Changeset to a Database</a></div>
 | 
						|
<div class="fancy-toc2"><a href="#inspecting_the_contents_of_a_changeset">3.3. Inspecting the Contents of a Changeset</a></div>
 | 
						|
<div class="fancy-toc1"><a href="#extended_functionality">4. Extended Functionality</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 id="introduction"><span>1. </span>Introduction</h1>
 | 
						|
 | 
						|
<p>The session extension provide a mechanism for recording changes
 | 
						|
to some or all of the <a href="rowidtable.html">rowid tables</a> in an SQLite database, and packaging
 | 
						|
those changes into a "changeset" or "patchset" file that can later
 | 
						|
be used to apply the same set of changes to another database with
 | 
						|
the same schema and compatible starting data.  A "changeset" may
 | 
						|
also be inverted and used to "undo" a session.
 | 
						|
 | 
						|
</p><p>This document is an introduction to the session extension.
 | 
						|
The details of the interface are in the separate
 | 
						|
<a href="session/intro.html">Session Extension C-language Interface</a> document.
 | 
						|
 | 
						|
</p><h2 id="typical_use_case"><span>1.1. </span>Typical Use Case</h2>
 | 
						|
 | 
						|
<p>Suppose SQLite is used as the <a href="appfileformat.html">application file format</a> for a
 | 
						|
particular design application.  Two users, Alice and Bob, each start
 | 
						|
with a baseline design that is about a gigabyte in size.  They work
 | 
						|
all day, in parallel, each making their own customizations and tweaks
 | 
						|
to the design.  At the end of the day, they would like to merge their
 | 
						|
changes together into a single unified design.
 | 
						|
 | 
						|
</p><p>The session extension facilitates this by recording all changes to
 | 
						|
both Alice's and Bob's databases and writing those changes into
 | 
						|
changeset or patchset files.  At the end of the day, Alice can send her
 | 
						|
changeset to Bob and Bob can "apply" it to his database.  The result (assuming
 | 
						|
there are no conflicts) is that Bob's database then contains both his
 | 
						|
changes and Alice's changes.  Likewise, Bob can send a changeset of
 | 
						|
his work over to Alice and she can apply his changes to her database.
 | 
						|
 | 
						|
</p><p>In other words, the session extension provides a facility for
 | 
						|
SQLite database files that is similar to the unix
 | 
						|
<a href="https://en.wikipedia.org/wiki/Patch_(Unix)">patch</a> utility program,
 | 
						|
or to the "merge" capabilities of version control systems such
 | 
						|
as <a href="https://www.fossil-scm.org/">Fossil</a>, <a href="https://git-scm.com">Git</a>, 
 | 
						|
or <a href="http://www.mercurial-scm.org/">Mercurial</a>.
 | 
						|
 | 
						|
</p><h2 id="obtaining_the_session_extension"><span>1.2. </span>Obtaining the Session Extension</h2>
 | 
						|
 | 
						|
<p> Since <a href="releaselog/3_13_0.html">version 3.13.0</a> (2016-05-18), 
 | 
						|
the session extension has been included in the SQLite
 | 
						|
<a href="amalgamation.html">amalgamation</a> source distribution. By default, the session extension is 
 | 
						|
disabled. To enable it, build with the following compiler switches:
 | 
						|
 | 
						|
</p><div class="codeblock"><pre>-DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK
 | 
						|
</pre></div>
 | 
						|
 | 
						|
<p> Or, if using the autoconf build system, pass the --enable-session option to the configure script.
 | 
						|
 | 
						|
</p><h2 id="limitations"><span>1.3. </span>Limitations</h2>
 | 
						|
 | 
						|
<ul>
 | 
						|
 | 
						|
<li><p> Prior to SQLite version 3.17.0, the session extension only worked with
 | 
						|
        <a href="rowidtable.html">rowid tables</a>, not <a href="withoutrowid.html">WITHOUT ROWID</a> tables. As of 3.17.0, both
 | 
						|
        rowid and WITHOUT ROWID tables are supported.
 | 
						|
 | 
						|
</p></li><li><p> There is no support for <a href="vtab.html">virtual tables</a>. Changes to virtual tables are
 | 
						|
        not captured.
 | 
						|
 | 
						|
</p></li><li><p> The session extension only works with tables that have a declared
 | 
						|
        PRIMARY KEY. The PRIMARY KEY of a table may be an INTEGER PRIMARY KEY
 | 
						|
        (rowid alias) or an external PRIMARY KEY.
 | 
						|
 | 
						|
</p></li><li><p> SQLite allows <a href="nulls.html">NULL values</a> to be stored in
 | 
						|
        PRIMARY KEY columns. However, the session extension ignores all
 | 
						|
        such rows. No changes affecting rows with one or more NULL values
 | 
						|
        in PRIMARY KEY columns are recorded by the sessions module.
 | 
						|
</p></li></ul>
 | 
						|
 | 
						|
<h1 id="concepts"><span>2. </span>Concepts</h1>
 | 
						|
 | 
						|
<a name="changeset"></a>
 | 
						|
 | 
						|
<h2 id="changesets_and_patchsets"><span>2.1. </span>Changesets and Patchsets</h2>
 | 
						|
<p> The sessions module revolves around creating and manipulating 
 | 
						|
changesets. A changeset is a blob of data that encodes a series of 
 | 
						|
changes to a database. Each change in a changeset is one of the 
 | 
						|
following:
 | 
						|
 | 
						|
</p><ul>
 | 
						|
  <li> <p>An <b>INSERT</b>. An INSERT change contains a single row to add to 
 | 
						|
       a database table. The payload of the INSERT change consists of the
 | 
						|
       values for each field of the new row.
 | 
						|
 | 
						|
  </p></li><li> <p>A <b>DELETE</b>. A DELETE change represents a row, identified by
 | 
						|
       its primary key values, to remove from a database table. The payload
 | 
						|
       of a DELETE change consists of the values for all fields of the 
 | 
						|
       deleted row.
 | 
						|
 | 
						|
  </p></li><li> <p>An <b>UPDATE</b>. An UPDATE change represents the modification of
 | 
						|
       one or more non-PRIMARY KEY fields of a single row within a database 
 | 
						|
       table, identified by its PRIMARY KEY fields. The payload for an UPDATE
 | 
						|
       change consists of:
 | 
						|
   </p><ul>
 | 
						|
     <li> The PRIMARY KEY values identifying the modified row, 
 | 
						|
     </li><li> The new values for each modified field of the row, and
 | 
						|
     </li><li> The original values for each modified field of the row.
 | 
						|
   </li></ul>
 | 
						|
       <p> An UPDATE change does not contain any information regarding
 | 
						|
       non-PRIMARY KEY fields that are not modified by the change. It is not
 | 
						|
       possible for an UPDATE change to specify modifications to PRIMARY 
 | 
						|
       KEY fields. 
 | 
						|
</p></li></ul>
 | 
						|
 | 
						|
<p> A single changeset may contain changes that apply to more than one 
 | 
						|
database table. For each table that the changeset includes at least one change
 | 
						|
for, it also encodes the following data:
 | 
						|
 | 
						|
</p><ul>
 | 
						|
  <li> The name of the database table, 
 | 
						|
  </li><li> The number of columns the table has, and
 | 
						|
  </li><li> Which of those columns are PRIMARY KEY columns.
 | 
						|
</li></ul>
 | 
						|
 | 
						|
<p> Changesets may only be applied to databases that contain tables 
 | 
						|
matching the above three criteria as stored in the changeset.
 | 
						|
 | 
						|
</p><p> A patchset is similar to a changeset. It is slightly more compact than
 | 
						|
a changeset, but provides more limited conflict detection and resolution
 | 
						|
options (see the next section for details). The differences between a 
 | 
						|
patchset and a changeset are that:
 | 
						|
 | 
						|
</p><ul>
 | 
						|
  <li><p> For a <b>DELETE</b> change, the payload consists of the PRIMARY KEY 
 | 
						|
          fields only. The original values of other fields are not stored as
 | 
						|
          part of a patchset.
 | 
						|
 | 
						|
  </p></li><li><p> For an <b>UPDATE</b> change, the payload consists of the PRIMARY KEY 
 | 
						|
          fields and the new values of modified fields only. The original
 | 
						|
          values of modified fields are not stored as part of a patchset.
 | 
						|
</p></li></ul>
 | 
						|
 | 
						|
<h2 id="conflicts"><span>2.2. </span>Conflicts</h2>
 | 
						|
 | 
						|
<p> When a changeset or patchset is applied to a database, an attempt is 
 | 
						|
made to insert a new row for each INSERT change, remove a row for each
 | 
						|
DELETE change and modify a row for each UPDATE change. If the target 
 | 
						|
database is in the same state as the original database that the changeset
 | 
						|
was recorded on, this is a simple matter. However, if the contents of the
 | 
						|
target database is not in exactly this state, conflicts can occur when
 | 
						|
applying the changeset or patchset.
 | 
						|
 | 
						|
</p><p>When processing an <b>INSERT</b> change, the following conflicts can
 | 
						|
occur:
 | 
						|
 | 
						|
</p><ul>
 | 
						|
  <li> The target database may already contain a row with the same PRIMARY
 | 
						|
       KEY values as specified by the INSERT change.
 | 
						|
 | 
						|
  </li><li> Some other database constraint, for example a UNIQUE or CHECK 
 | 
						|
       constraint, may be violated when the new row is inserted.
 | 
						|
</li></ul>
 | 
						|
 | 
						|
<p>When processing a <b>DELETE</b> change, the following conflicts may be
 | 
						|
detected:
 | 
						|
 | 
						|
</p><ul>
 | 
						|
  <li> The target database may contain no row with the specified PRIMARY 
 | 
						|
       KEY values to delete.
 | 
						|
 | 
						|
  </li><li> The target database may contain a row with the specified PRIMARY
 | 
						|
       KEY values, but the other fields may contain values that do not
 | 
						|
       match those stored as part of the changeset. This type of conflict
 | 
						|
       is not detected when using a patchset.
 | 
						|
</li></ul>
 | 
						|
 | 
						|
<p>When processing an <b>UPDATE</b> change, the following conflicts may be
 | 
						|
detected:
 | 
						|
 | 
						|
</p><ul>
 | 
						|
  <li> The target database may contain no row with the specified PRIMARY 
 | 
						|
       KEY values to modify.
 | 
						|
 | 
						|
  </li><li> The target database may contain a row with the specified PRIMARY
 | 
						|
       KEY values, but the current values of the fields that will be modified
 | 
						|
       by the change may not match the original values stored within the
 | 
						|
       changeset. This type of conflict is not detected when using a patchset.
 | 
						|
 | 
						|
  </li><li> Some other database constraint, for example a UNIQUE or CHECK 
 | 
						|
       constraint, may be violated when the row is updated.
 | 
						|
</li></ul>
 | 
						|
 | 
						|
<p> Depending on the type of conflict, a sessions application has a variety
 | 
						|
of configurable options for dealing with conflicts, ranging from omitting the
 | 
						|
conflicting change, aborting the entire changeset application or applying
 | 
						|
the change despite the conflict. For details, refer to the documentation for
 | 
						|
the <a href="session/sqlite3changeset_apply.html">sqlite3changeset_apply()</a> API.
 | 
						|
 | 
						|
</p><h2 id="changeset_construction"><span>2.3. </span>Changeset Construction</h2>
 | 
						|
 | 
						|
<p> After a session object has been configured, it begins monitoring for 
 | 
						|
changes to its configured tables. However, it does not record an entire
 | 
						|
change each time a row within the database is modified. Instead, it records
 | 
						|
just the PRIMARY KEY fields for each inserted row, and just the PRIMARY KEY 
 | 
						|
and all original row values for any updated or deleted rows. If a row is 
 | 
						|
modified more than once by a single session, no new information is recorded.
 | 
						|
 | 
						|
</p><p> The other information required to create a changeset or patchset is
 | 
						|
read from the database file when <a href="session/sqlite3session_changeset.html">sqlite3session_changeset()</a> or
 | 
						|
<a href="session/sqlite3session_patchset.html">sqlite3session_patchset()</a> is called. Specifically,
 | 
						|
 | 
						|
</p><ul>
 | 
						|
  <li> <p>For each primary key recorded as a result of an INSERT operation, 
 | 
						|
       the sessions module checks if there is a row with a matching primary
 | 
						|
       key still in the table. If so, an INSERT change is added to the 
 | 
						|
       changeset.
 | 
						|
 | 
						|
  </p></li><li> <p>For each primary key recorded as a result of an UPDATE or DELETE
 | 
						|
       operation, the sessions module also checks for a row with a matching
 | 
						|
       primary key within the table. If one can be found, but one or more
 | 
						|
       of the non-PRIMARY KEY fields does not match the original recorded
 | 
						|
       value, an UPDATE is added to the changeset. Or, if there is no row
 | 
						|
       at all with the specified primary key, a DELETE is added to the 
 | 
						|
       changeset. If the row does exist but none of the non-PRIMARY KEY
 | 
						|
       fields have been modified, no change is added to the changeset.
 | 
						|
</p></li></ul>
 | 
						|
 | 
						|
<p> One implication of the above is that if a change is made and then 
 | 
						|
unmade within a single session (for example if a row is inserted and then
 | 
						|
deleted again), the sessions module does not report any change at all. Or
 | 
						|
if a row is updated multiple times within the same session, all updates
 | 
						|
are coalesced into a single update within any changeset or patchset blob.
 | 
						|
 | 
						|
</p><h1 id="using_the_session_extension"><span>3. </span>Using The Session Extension</h1>
 | 
						|
 | 
						|
<p> This section provides examples that demonstrate how to use the sessions
 | 
						|
    extension.
 | 
						|
 | 
						|
</p><h2 id="capturing_a_changeset"><span>3.1. </span>Capturing a Changeset</h2>
 | 
						|
 | 
						|
<p> The example code below demonstrates the steps involved in capturing a
 | 
						|
changeset while executing SQL commands. In summary:
 | 
						|
 | 
						|
</p><ol>
 | 
						|
  <li> <p>A session object (type sqlite3_session*) is created by making a 
 | 
						|
          call to the <a href="session/sqlite3session_create.html">sqlite3session_create()</a> API function.
 | 
						|
 | 
						|
       </p><p>A single session object monitors changes made to a single database 
 | 
						|
          (i.e. "main", "temp" or an attached database) via a single 
 | 
						|
          sqlite3* database handle.
 | 
						|
 | 
						|
  </p></li><li> <p>The session object is configured with a set of tables to monitor
 | 
						|
          changes on.
 | 
						|
 | 
						|
       </p><p> By default a session object does not monitor changes on any 
 | 
						|
           database table. Before it does so it must be configured. There 
 | 
						|
           are three ways to configure the set of tables to monitor changes
 | 
						|
           on:
 | 
						|
       </p><ul>
 | 
						|
         <li> By explicitly specifying tables using one call to
 | 
						|
              <a href="session/sqlite3session_attach.html">sqlite3session_attach()</a> for each table, or
 | 
						|
 | 
						|
         </li><li> By specifying that all tables in the database should be monitored
 | 
						|
              for changes using a call to <a href="session/sqlite3session_attach.html">sqlite3session_attach()</a> with a
 | 
						|
              NULL argument, or
 | 
						|
 | 
						|
         </li><li> By configuring a callback to be invoked the first time each table
 | 
						|
              is written to that indicates to the session module whether or
 | 
						|
              not changes on the table should be monitored.
 | 
						|
       </li></ul>
 | 
						|
        <p> The example code below uses the second of the methods enumerated
 | 
						|
            above - it monitors for changes on all database tables.
 | 
						|
 | 
						|
  </p></li><li> <p> Changes are made to the database by executing SQL statements. The
 | 
						|
           session object records these changes.
 | 
						|
 | 
						|
  </p></li><li> <p> A changeset blob is extracted from the session object using a call
 | 
						|
           to <a href="session/sqlite3session_changeset.html">sqlite3session_changeset()</a> (or, if using patchsets, a call to
 | 
						|
           the <a href="session/sqlite3session_patchset.html">sqlite3session_patchset()</a> function).
 | 
						|
 | 
						|
  </p></li><li> <p> The session object is deleted using a call to the 
 | 
						|
           <a href="session/sqlite3session_delete.html">sqlite3session_delete()</a> API function.
 | 
						|
 | 
						|
       </p><p> It is not necessary to delete a session object after extracting
 | 
						|
           a changeset or patchset from it. It can be left attached to the
 | 
						|
           database handle and will continue monitoring for changes on the
 | 
						|
           configured tables as before. However, if 
 | 
						|
           <a href="session/sqlite3session_changeset.html">sqlite3session_changeset()</a> or <a href="session/sqlite3session_patchset.html">sqlite3session_patchset()</a> is
 | 
						|
           called a second time on a session object, the changeset or patchset
 | 
						|
           will contain <em>all</em> changes that have taken place on the connection
 | 
						|
           since the session was created. In other words,
 | 
						|
           a session object is not reset or
 | 
						|
           zeroed by a call to sqlite3session_changeset() or
 | 
						|
           sqlite3session_patchset().
 | 
						|
</p></li></ol>
 | 
						|
 | 
						|
<div class="codeblock"><pre><i>/*
 | 
						|
** Argument zSql points to a buffer containing an SQL script to execute 
 | 
						|
** against the database handle passed as the first argument. As well as
 | 
						|
** executing the SQL script, this function collects a changeset recording
 | 
						|
** all changes made to the "main" database file. Assuming no error occurs,
 | 
						|
** output variables (*ppChangeset) and (*pnChangeset) are set to point
 | 
						|
** to a buffer containing the changeset and the size of the changeset in
 | 
						|
** bytes before returning SQLITE_OK. In this case it is the responsibility
 | 
						|
** of the caller to eventually free the changeset blob by passing it to
 | 
						|
** the sqlite3_free function.
 | 
						|
**
 | 
						|
** Or, if an error does occur, return an SQLite error code. The final
 | 
						|
** value of (*pChangeset) and (*pnChangeset) are undefined in this case.
 | 
						|
*/</i>
 | 
						|
int sql_exec_changeset(
 | 
						|
  sqlite3 *db,                  <i>/* Database handle */</i>
 | 
						|
  const char *zSql,             <i>/* SQL script to execute */</i>
 | 
						|
  int *pnChangeset,             <i>/* OUT: Size of changeset blob in bytes */</i>
 | 
						|
  void **ppChangeset            <i>/* OUT: Pointer to changeset blob */</i>
 | 
						|
){
 | 
						|
  sqlite3_session *pSession = 0;
 | 
						|
  int rc;
 | 
						|
 | 
						|
  <i>/* Create a new session object */</i>
 | 
						|
  rc = sqlite3session_create(db, "main", &pSession);
 | 
						|
 | 
						|
  <i>/* Configure the session object to record changes to all tables */</i>
 | 
						|
  if( rc==SQLITE_OK ) rc = sqlite3session_attach(pSession, NULL);
 | 
						|
 | 
						|
  <i>/* Execute the SQL script */</i>
 | 
						|
  if( rc==SQLITE_OK ) rc = sqlite3_exec(db, zSql, 0, 0, 0);
 | 
						|
 | 
						|
  <i>/* Collect the changeset */</i>
 | 
						|
  if( rc==SQLITE_OK ){
 | 
						|
    rc = sqlite3session_changeset(pSession, pnChangeset, ppChangeset);
 | 
						|
  }
 | 
						|
 | 
						|
  <i>/* Delete the session object */</i>
 | 
						|
  sqlite3session_delete(pSession);
 | 
						|
 | 
						|
  return rc;
 | 
						|
}
 | 
						|
</pre></div>
 | 
						|
 | 
						|
<h2 id="applying_a_changeset_to_a_database"><span>3.2. </span>Applying a Changeset to a Database</h2>
 | 
						|
 | 
						|
<p> Applying a changeset to a database is simpler than capturing a changeset.
 | 
						|
Usually, a single call to <a href="session/sqlite3changeset_apply.html">sqlite3changeset_apply()</a>, as depicted in the
 | 
						|
example code below, suffices.
 | 
						|
 | 
						|
</p><p> In cases where it is complicated, the complications in applying a 
 | 
						|
changeset lie in conflict resolution. Refer to the API documentation linked
 | 
						|
above for details.
 | 
						|
 | 
						|
  </p><div class="codeblock"><pre><i>/*
 | 
						|
** Conflict handler callback used by apply_changeset(). See below.
 | 
						|
*/</i>
 | 
						|
static int xConflict(void *pCtx, int eConflict, sqlite3_changset_iter *pIter){
 | 
						|
  int ret = (int)pCtx;
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
<i>/*
 | 
						|
** Apply the changeset contained in blob pChangeset, size nChangeset bytes,
 | 
						|
** to the main database of the database handle passed as the first argument.
 | 
						|
** Return SQLITE_OK if successful, or an SQLite error code if an error
 | 
						|
** occurs.
 | 
						|
**
 | 
						|
** If parameter bIgnoreConflicts is true, then any conflicting changes 
 | 
						|
** within the changeset are simply ignored. Or, if bIgnoreConflicts is
 | 
						|
** false, then this call fails with an SQLTIE_ABORT error if a changeset
 | 
						|
** conflict is encountered.
 | 
						|
*/</i>
 | 
						|
int apply_changeset(
 | 
						|
  sqlite3 *db,                  <i>/* Database handle */</i>
 | 
						|
  int bIgnoreConflicts,         <i>/* True to ignore conflicting changes */</i>
 | 
						|
  int nChangeset,               <i>/* Size of changeset in bytes */</i>
 | 
						|
  void *pChangeset              <i>/* Pointer to changeset blob */</i>
 | 
						|
){
 | 
						|
  return sqlite3changeset_apply(
 | 
						|
      db, 
 | 
						|
      nChangeset, pChangeset, 
 | 
						|
      0, xConflict, 
 | 
						|
      (void*)bIgnoreConflicts
 | 
						|
  );
 | 
						|
}
 | 
						|
</pre></div>
 | 
						|
 | 
						|
<h2 id="inspecting_the_contents_of_a_changeset"><span>3.3. </span>Inspecting the Contents of a Changeset</h2>
 | 
						|
 | 
						|
<p> The example code below demonstrates the techniques used to iterate 
 | 
						|
through and extract the data related to all changes in a changeset. To
 | 
						|
summarize:
 | 
						|
 | 
						|
</p><ol>
 | 
						|
  <li><p> The <a href="session/sqlite3changeset_start.html">sqlite3changeset_start()</a> API is called to create and
 | 
						|
          initialize an iterator to iterate through the contents of a
 | 
						|
          changeset. Initially, the iterator points to no element at all.
 | 
						|
 | 
						|
  </p></li><li><p> The first call to <a href="session/sqlite3changeset_next.html">sqlite3changeset_next()</a> on the iterator moves
 | 
						|
          it to point to the first change  in the changeset (or to EOF, if 
 | 
						|
          the changeset is completely empty). sqlite3changeset_next() returns
 | 
						|
          SQLITE_ROW if it moves the iterator to point to a valid entry,
 | 
						|
          SQLITE_DONE if it moves the iterator to EOF, or an SQLite error
 | 
						|
          code if an error occurs.
 | 
						|
 | 
						|
  </p></li><li><p> If the iterator points to a valid entry, the <a href="session/sqlite3changeset_op.html">sqlite3changeset_op()</a>
 | 
						|
          API may be used to determine the type of change (INSERT, UPDATE or
 | 
						|
          DELETE) that the iterator points to. Additionally, the same API 
 | 
						|
          can be used to obtain the name of the table the change applies to
 | 
						|
          and its expected number of columns and primary key columns.
 | 
						|
 | 
						|
  </p></li><li><p> If the iterator points to a valid INSERT or UPDATE entry, the
 | 
						|
          <a href="session/sqlite3changeset_new.html">sqlite3changeset_new()</a> API may be used to obtain the new.* values
 | 
						|
          within the change payload.
 | 
						|
 | 
						|
  </p></li><li><p> If the iterator points to a valid DELETE or UPDATE entry, the
 | 
						|
          <a href="session/sqlite3changeset_old.html">sqlite3changeset_old()</a> API may be used to obtain the old.* values
 | 
						|
          within the change payload.
 | 
						|
 | 
						|
  </p></li><li><p> An iterator is deleted using a call to the 
 | 
						|
          <a href="session/sqlite3changeset_finalize.html">sqlite3changeset_finalize()</a> API. If an error occured while
 | 
						|
          iterating, an SQLite error code is returned (even if the same error
 | 
						|
          code has already been returned by sqlite3changeset_next()). Or,
 | 
						|
          if no error has occurred, SQLITE_OK is returned.
 | 
						|
</p></li></ol>
 | 
						|
 | 
						|
  <div class="codeblock"><pre><i>/*
 | 
						|
** Print the contents of the changeset to stdout.
 | 
						|
*/</i>
 | 
						|
static int print_changeset(void *pChangeset, int nChangeset){
 | 
						|
  int rc;
 | 
						|
  sqlite3_changeset_iter *pIter = 0;
 | 
						|
 | 
						|
  <i>/* Create an iterator to iterate through the changeset */</i>
 | 
						|
  rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
 | 
						|
  if( rc!=SQLITE_OK ) return rc;
 | 
						|
 | 
						|
  <i>/* This loop runs once for each change in the changeset */</i>
 | 
						|
  while( SQLITE_ROW==sqlite3changeset_next(pIter) ){
 | 
						|
    const char *zTab;           <i>/* Table change applies to */</i>
 | 
						|
    int nCol;                   <i>/* Number of columns in table zTab */</i>
 | 
						|
    int op;                     <i>/* SQLITE_INSERT, UPDATE or DELETE */</i>
 | 
						|
    sqlite3_value *pVal;
 | 
						|
 | 
						|
    <i>/* Print the type of operation and the table it is on */</i>
 | 
						|
    rc = sqlite3changeset_op(pIter, &zTab, &nCol, &op, 0);
 | 
						|
    if( rc!=SQLITE_OK ) goto exit_print_changeset;
 | 
						|
    printf("%s on table %s\n",
 | 
						|
      op==SQLITE_INSERT?"INSERT" : op==SQLITE_UPDATE?"UPDATE" : "DELETE",
 | 
						|
      zTab
 | 
						|
    );
 | 
						|
 | 
						|
    <i>/* If this is an UPDATE or DELETE, print the old.* values */</i>
 | 
						|
    if( op==SQLITE_UPDATE || op==SQLITE_DELETE ){
 | 
						|
      printf("Old values:");
 | 
						|
      for(i=0; i<nCol; i++){
 | 
						|
        rc = sqlite3changeset_old(pIter, i, &pVal);
 | 
						|
        if( rc!=SQLITE_OK ) goto exit_print_changeset;
 | 
						|
        printf(" %s", pVal ? sqlite3_value_text(pVal) : "-");
 | 
						|
      }
 | 
						|
      printf("\n");
 | 
						|
    }
 | 
						|
 | 
						|
    <i>/* If this is an UPDATE or INSERT, print the new.* values */</i>
 | 
						|
    if( op==SQLITE_UPDATE || op==SQLITE_INSERT ){
 | 
						|
      printf("New values:");
 | 
						|
      for(i=0; i<nCol; i++){
 | 
						|
        rc = sqlite3changeset_new(pIter, i, &pVal);
 | 
						|
        if( rc!=SQLITE_OK ) goto exit_print_changeset;
 | 
						|
        printf(" %s", pVal ? sqlite3_value_text(pVal) : "-");
 | 
						|
      }
 | 
						|
      printf("\n");
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  <i>/* Clean up the changeset and return an error code (or SQLITE_OK) */</i>
 | 
						|
 exit_print_changeset:
 | 
						|
  rc2 = sqlite3changeset_finalize(pIter);
 | 
						|
  if( rc==SQLITE_OK ) rc = rc2;
 | 
						|
  return rc;
 | 
						|
}
 | 
						|
</pre></div>
 | 
						|
 | 
						|
<h1 id="extended_functionality"><span>4. </span>Extended Functionality</h1>
 | 
						|
 | 
						|
<p> Most applications will only use the session module functionality described
 | 
						|
in the previous section. However, the following additional functionality is
 | 
						|
available for the use and manipulation of changeset and patchset blobs:
 | 
						|
 | 
						|
</p><ul>
 | 
						|
  <li> <p>Two or more changeset/patchsets may be combined using the 
 | 
						|
       <a href="session/sqlite3changeset_concat.html">sqlite3changeset_concat()</a> or <a href="session/changegroup.html">sqlite3_changegroup</a> interfaces.
 | 
						|
 | 
						|
  </p></li><li> <p>A changeset may be "inverted" using the <a href="session/sqlite3changeset_invert.html">sqlite3changeset_invert()</a>
 | 
						|
       API function. An inverted changeset undoes the changes made by the
 | 
						|
       original. If changeset C<sup>+</sup> is the inverse of changeset C, then
 | 
						|
       applying C and then C<sup>+</sup> to a database should leave
 | 
						|
       the database unchanged.
 | 
						|
</p></li></ul>
 |