Documentation Source Text

Check-in [c8d92d73ec]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Update to session module documentation.
Timelines: family | ancestors | descendants | both | experimental
Files: files | file ages | folders
SHA1: c8d92d73ec2d8984a4d2069d91b32c6efc371279
User & Date: dan 2016-08-29 14:46:14
Context
2016-08-29
15:01
Merge latest trunk changes into experimental branch. check-in: b39f7479c0 user: dan tags: experimental
14:46
Update to session module documentation. check-in: c8d92d73ec user: dan tags: experimental
2016-08-26
18:52
Fix a bug in the previous checkin. check-in: 92ed3a43f0 user: dan tags: experimental
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to pages/fancyformat.tcl.

   375    375   }
   376    376   
   377    377   proc addtoc {zDoc} {
   378    378     # If the extension with the [parsehtml] command has not been loaded,
   379    379     # load it now.
   380    380     #
   381    381     if {[info commands parsehtml] == ""} { load ./parsehtml.so }
   382         -
   383    382   
   384    383     # Handle any <tclscript> blocks.
   385    384     #
   386    385     while { [regexp -nocase {<tclscript>(.*?)</tclscript>} $zDoc -> script] } {
   387    386       set sub [eval $script]
   388    387       set sub [string map {& {\&}} $sub]
   389    388       set zDoc [regsub -nocase {<tclscript>.*?</tclscript>} $zDoc $sub]

Changes to pages/session.in.

     1      1   <title>SQLite Session Module C/C++ Interface</title>
     2      2   
     3      3   <h2>SQLite Session Module C/C++ Interface</h2>
     4      4   
     5      5   <tcl>
     6      6   file mkdir ${DEST}/session
     7      7   set hdrfile [file join $SRC ext session sqlite3session.h]
     8         -if {[file exists $hdrfile]} {
     9         -  set in [open $hdrfile]
    10         -} else {
    11         -  set in [open /dev/null]
    12         -}
            8  +set in [open $hdrfile]
            9  +
    13     10   set title {}       ;# title of a section of interface definition
    14     11   set type {}        ;# one of: constant datatype function
    15     12   set body {}        ;# human-readable description
    16     13   set code {}        ;# C code of the definition
    17     14   set phase 0        ;# Phase used by the parser 
    18     15   set content {}     ;# List of records, one record per definition
    19     16   set dcnt 0         ;# Number of individual declarations
................................................................................
    56     53     global intab body
    57     54     endtab
    58     55     append body {<table border="0" cellpadding="5" cellspacing="0">}
    59     56     append body \n
    60     57     set intab 1
    61     58   }
    62     59   
    63         -# Read sqlite3.c line by line and extract interface definition
    64         -# information (found in what was originally sqlite3.h).
           60  +# Read sqlite3session.h line by line and extract interface 
           61  +# definition information.
    65     62   #
    66     63   while {![eof $in]} {
    67     64     set line [gets $in]
    68     65     incr lineno
    69     66     if {$phase==0} {
    70     67       # Looking for the CAPI3REF: keyword.  This marks the beginning of
    71     68       # an interface definition.  When the CAPI3REF keywords is seen, 
................................................................................
   246    243   <p>The content on these pages is extracted from comments
   247    244   in the source code.</p>
   248    245   
   249    246   <p>The interface is broken down into three categories:</p>
   250    247   
   251    248   <ol>
   252    249   <li><p><a href="objlist.html"><b>List Of Objects.</b></a>
   253         -    This is a list of the two abstract objects used by the SQLite session
          250  +    This is a list of the three abstract objects used by the SQLite session
   254    251       module.
   255    252       
   256    253   
   257    254   <li><p><a href="constlist.html"><b>List Of Constants.</b></a>
   258    255       This is a list of numeric constants used by the SQLite session module
   259    256       and represented by #defines in the sqlite3session.h header file. There
   260    257       are constants passed to conflict handler callbacks to indicate the type
................................................................................
   313    310         lappend objlist [list $k $kw $s]
   314    311       }
   315    312     }
   316    313   }
   317    314   hd_open_aux session/objlist.html
   318    315   hd_header {List Of SQLite Objects}
   319    316   hd_enable_main 0
   320         -hd_putsnl {<a href="intro.html"><h2>SQLite C Interface</h2></a>}
          317  +hd_putsnl {<a href="intro.html"><h2>Session Module C Interface</h2></a>}
   321    318   hd_enable_main 1
   322    319   </tcl>
   323    320   <h2>Objects:</h2>
   324    321   <tcl>
   325    322   output_list 3 [lsort $objlist]
   326    323   hd_enable_main 0
   327    324   hd_putsnl {<p>Other lists:
................................................................................
   345    342         lappend clist [list $k $kw $s]
   346    343       }
   347    344     }
   348    345   }
   349    346   hd_open_aux session/constlist.html
   350    347   hd_header {List Of SQLite Constants}
   351    348   hd_enable_main 0
   352         -hd_putsnl {<a href="intro.html"><h2>SQLite C Interface</h2></a>}
          349  +hd_putsnl {<a href="intro.html"><h2>Session Module C Interface</h2></a>}
   353    350   hd_enable_main 1
   354    351   </tcl>
   355    352   <h2>Constants:</h2>
   356    353   <tcl>
   357    354   set clist [lsort -index 1 $clist]
   358    355   output_list 2 $clist
   359    356   hd_enable_main 0
................................................................................
   380    377       }
   381    378     }
   382    379   }
   383    380   hd_open_aux session/funclist.html
   384    381   hd_header {List Of SQLite Functions}
   385    382   hd_keywords *session_funclist {Session Module C-API function list}
   386    383   hd_enable_main 0
   387         -hd_putsnl {<a href="intro.html"><h2>SQLite C Interface</h2></a>}
          384  +hd_putsnl {<a href="intro.html"><h2>Session Module C Interface</h2></a>}
   388    385   hd_enable_main 1
   389    386   </tcl>
   390    387   <h2>Functions:</h2>
   391    388   <tcl>
   392    389   set funclist [lsort -index 1 $funclist]
   393    390   output_list 3 $funclist
   394    391   hd_enable_main 0
................................................................................
   405    402   foreach c [lsort $content] {
   406    403     foreach {key title type keywords body code} $c break
   407    404     set kw [preferred_keyword [lsort $keywords]]
   408    405     hd_fragment $kw
   409    406     hd_open_aux session/[convert_keyword_to_filename $kw]
   410    407     hd_header $title
   411    408     hd_enable_main 0
   412         -  hd_puts {<a href="intro.html"><h2>SQLite C Interface</h2></a>}
          409  +  hd_puts {<a href="intro.html"><h2>Session Module C Interface</h2></a>}
   413    410     hd_enable_main 1
   414    411     eval hd_keywords $keywords
   415    412   
   416    413     hd_puts "<h2>$title</h2>"
   417    414     hd_puts "<blockquote><pre>"
   418    415     hd_puts "$code"
   419    416     hd_puts "</pre></blockquote>"

Changes to pages/sessionintro.in.

     1      1   <title>The Session Extension</title>
     2      2   <tcl>
     3      3   hd_keywords {session} {session extension}
     4      4   </tcl>
     5         -<h1 align="center">The Session Extension</h1>
     6      5   
     7         -<h2>1.0 Introduction</h2>
            6  +<table_of_contents>
            7  +
            8  +<h1>Introduction</h1>
     8      9   
     9     10   <p>The session extension provide a mechanism for recording changes
    10     11   to some or all of the [rowid tables] in an SQLite database, and packaging
    11     12   those changes into a "changeset" or "patchset" file that can later
    12     13   be used to apply the same set of changes to another database with
    13     14   the same schema and compatible starting data.  A "changeset" may
    14     15   also be inverted and used to "undo" a session.
    15     16   
    16     17   <p>This document is an introduction to the session extension.
    17     18   The details of the interface are in the separate
    18     19   [Session Extension C-language Interface] document.
    19     20   
    20         -<h3>1.1 Typical Use Case</h3>
           21  +<h2>Typical Use Case</h2>
    21     22   
    22     23   <p>Suppose SQLite is used as the [application file format] for a
    23     24   particular design application.  Two users, Alice and Bob, each start
    24     25   with a baseline design that is about a gigabyte in size.  They work
    25     26   all day, in parallel, each making their own customizations and tweaks
    26     27   to the design.  At the end of the day, they would like to merge their
    27     28   changes together into a single unified design.
................................................................................
    37     38   <p>In other words, the session extension provides a facility for
    38     39   SQLite database files that is similar to the unix
    39     40   [https://en.wikipedia.org/wiki/Patch_(Unix)|patch] utility program,
    40     41   or to the "merge" capabilities of version control systems such
    41     42   as [https://www.fossil-scm.org/|Fossil], [https://git-scm.com|Git], 
    42     43   or [http://www.mercurial-scm.org/|Mercurial].
    43     44   
    44         -<h3>1.2 Requirements And Limitations</h3>
           45  +<h2>Obtaining the Session Extension</h2>
           46  +
           47  +<p> Since version 3.13, the session extension has been included in the SQLite
           48  +[amalgamation] source distribution. By default, the session extension is 
           49  +disabled. To enable it, build with the following compiler switches:
           50  +
           51  +<codeblock>
           52  +  -DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK
           53  +</codeblock>
           54  +
           55  +<p> Or, if using the autoconf build system, pass the --enable-session option to the configure script.
           56  +
           57  +<h2>Limitations</h2>
           58  +
           59  +<ul>
           60  +<li><p> The session extension only works with [rowid tables].  Changes 
           61  +        to [WITHOUT ROWID] tables and [virtual tables] are not captured.
           62  +
           63  +<li><p> The session extension only works with tables that have a declared
           64  +        PRIMARY KEY. The PRIMARY KEY of a table may be an INTEGER PRIMARY KEY
           65  +        (rowid alias) or an external PRIMARY KEY.
           66  +
           67  +<li><p> SQLite allows <a href=nulls.html>NULL values</a> to be stored in
           68  +        PRIMARY KEY columns. However, the session extension ignores all
           69  +        such rows. No changes affecting rows with one or more NULL values
           70  +        in PRIMARY KEY columns are recorded by the sessions module.
           71  +</ul>
           72  +
           73  +<h1>Concepts</h2>
           74  +<h2 id=changesets_and_patchsets>Changesets and Patchsets</h2>
           75  +<p> The sessions module revolves around creating and manipulating 
           76  +changesets. A changeset is a blob of data that encodes a series of 
           77  +changes to a database. Each change in a changeset is one of the 
           78  +following:
           79  +
           80  +<ul>
           81  +  <li> <p>An <b>INSERT</b>. An INSERT change contains a single row to add to 
           82  +       a database table. The payload of the INSERT change consists of the
           83  +       values for each field of the new row.
           84  +
           85  +  <li> <p>A <b>DELETE</b>. A DELETE change represents a row, identified by
           86  +       its primary key values, to remove from a database table. The payload
           87  +       of a DELETE change consists of the values for all fields of the 
           88  +       deleted row.
           89  +
           90  +  <li> <p>An <b>UPDATE</b>. An UPDATE change represents the modification of
           91  +       one or more non-PRIMARY KEY fields of a single row within a database 
           92  +       table, identified by its PRIMARY KEY fields. The payload for an UPDATE
           93  +       change consists of:
           94  +   <ul>
           95  +     <li> The PRIMARY KEY values identifying the modified row, 
           96  +     <li> The new values for each modified field of the row, and
           97  +     <li> The original values for each modified field of the row.
           98  +   </ul>
           99  +       <p> An UPDATE change does not contain any information regarding
          100  +       non-PRIMARY KEY fields that are not modified by the change. It is not
          101  +       possible for an UPDATE change to specify modifications to PRIMARY 
          102  +       KEY fields. 
          103  +</ul>
          104  +
          105  +<p> A single changeset may contain changes that apply to more than one 
          106  +database table. For each table that the changeset includes at least one change
          107  +for, it also encodes the following data:
          108  +
          109  +<ul>
          110  +  <li> The name of the database table, 
          111  +  <li> The number of columns the table has, and
          112  +  <li> Which of those columns are PRIMARY KEY columns.
          113  +</ul>
          114  +
          115  +<p> Changesets may only be applied to databases that contain tables 
          116  +matching the above three criteria as stored in the changeset.
          117  +
          118  +<p> A patchset is similar to a changeset. It is slightly more compact than
          119  +a changeset, but provides more limited conflict detection and resolution
          120  +options (see the next section for details). The differences between a 
          121  +patchset and a changeset are that:
          122  +
          123  +<ul>
          124  +  <li><p> For a <b>DELETE</b> change, the payload consists of the PRIMARY KEY 
          125  +          fields only. The original values of other fields are not stored as
          126  +          part of a patchset.
          127  +
          128  +  <li><p> For an <b>UPDATE</b> change, the payload consists of the PRIMARY KEY 
          129  +          fields and the new values of modified fields only. The original
          130  +          values of modified fields are not stored as part of a patchset.
          131  +</ul>
          132  +
          133  +<h2>Conflicts</h2>
          134  +
          135  +<p> When a changset or patchset is applied to a database, an attempt is 
          136  +made to insert a new row for each INSERT change, remove a row for each
          137  +DELETE change and modify a row for each UPDATE change. If the target 
          138  +database is in the same state as the original database that the changeset
          139  +was recorded on, this is a simple matter. However, if the contents of the
          140  +target database is not in exactly this state, conflicts can occur when
          141  +applying the changeset or patchset.
          142  +
          143  +<p>When processing an <b>INSERT</b> change, the following conflicts can
          144  +occur:
          145  +
          146  +<ul>
          147  +  <li> The target database may already contain a row with the same PRIMARY
          148  +       KEY values as specified by the INSERT change.
          149  +
          150  +  <li> Some other database constraint, for example a UNIQUE or CHECK 
          151  +       constraint, may be violated when the new row is inserted.
          152  +</ul>
          153  +
          154  +<p>When processing a <b>DELETE</b> change, the following conflicts may be
          155  +detected:
          156  +
          157  +<ul>
          158  +  <li> The target database may contain no row with the specified PRIMARY 
          159  +       KEY values to delete.
          160  +
          161  +  <li> The target database may contain a row with the specified PRIMARY
          162  +       KEY values, but the other fields may contain values that do not
          163  +       match those stored as part of the changeset. This type of conflict
          164  +       is not detected when using a patchset.
          165  +</ul>
          166  +
          167  +<p>When processing an <b>UPDATE</b> change, the following conflicts may be
          168  +detected:
          169  +
          170  +<ul>
          171  +  <li> The target database may contain no row with the specified PRIMARY 
          172  +       KEY values to modify.
          173  +
          174  +  <li> The target database may contain a row with the specified PRIMARY
          175  +       KEY values, but the current values of the fields that will be modified
          176  +       by the change may not match the original values stored within the
          177  +       changeset. This type of conflict is not detected when using a patchset.
          178  +
          179  +  <li> Some other database constraint, for example a UNIQUE or CHECK 
          180  +       constraint, may be violated when the row is updated.
          181  +</ul>
          182  +
          183  +<p> Depending on the type of conflict, a sessions application has a variety
          184  +of configurable options for dealing with conflicts, ranging from omitting the
          185  +conflicting change, aborting the entire changeset application or applying
          186  +the change despite the conflict. For details, refer to the documentation for
          187  +the [sqlite3changeset_apply()] API.
          188  +
          189  +<h2>Changeset Construction</h2>
          190  +
          191  +<p> After a session object has been configured, it begins monitoring for 
          192  +changes to its configured tables. However, it does not record an entire
          193  +change each time a row within the database is modified. Instead, it records
          194  +just the PRIMARY KEY fields for each inserted row, and just the PRIMARY KEY 
          195  +and all original row values for any updated or deleted rows. If a row is 
          196  +modified more than once by a single session, no new information is recorded.
          197  +
          198  +<p> The other information required to create a changeset or patchset is
          199  +read from the database file when [sqlite3session_changeset()] or
          200  +[sqlite3session_patchset()] is called. Specifically,
    45    201   
    46    202   <ul>
    47         -<li><p>
    48         -The session extension requires the [SQLITE_ENABLE_PREUPDATE_HOOK]
    49         -compile-time option to enable the preupdate hook interfaces.
          203  +  <li> <p>For each primary key recorded as a result of an INSERT operation, 
          204  +       the sessions module checks if there is a row with a matching primary
          205  +       key still in the table. If so, an INSERT change is added to the 
          206  +       changeset.
    50    207   
    51         -<li><p>
    52         -All of the session extension code is omitted from the build unless
    53         -the [SQLITE_ENABLE_SESSION] compile-time option is used.
    54         -
    55         -<li><p>
    56         -The session extension only works with [rowid tables].  Changes to
    57         -[WITHOUT ROWID] tables and [virtual tables] are not captured.
          208  +  <li> <p>For each primary key recorded as a result of an UPDATE or DELETE
          209  +       operation, the sessions module also checks for a row with a matching
          210  +       primary key within the table. If one can be found, but one or more
          211  +       of the non-PRIMARY KEY fields does not match the original recorded
          212  +       value, an UPDATE is added to the changeset. Or, if there is no row
          213  +       at all with the specified primary key, a DELETE is added to the 
          214  +       changeset. If the row does exist but none of the non-PRIMARY KEY
          215  +       fields have been modified, no change is added to the changeset.
    58    216   </ul>
    59    217   
    60         -<h2>2.0 Using The Session Extension</h2>
          218  +<p> One implication of the above is that if a change is made and then 
          219  +unmade within a single session (for example if a row is inserted and then
          220  +deleted again), the sessions module does not report any change at all. Or
          221  +if a row is updated multiple times within the same session, all updates
          222  +are coalesced into a single update within any changeset or patchset blob.
          223  +
          224  +<h1>Using The Session Extension</h1>
          225  +
          226  +<p> This section provides examples that demonstrate how to use the sessions
          227  +    extension.
          228  +
          229  +<h2>Capturing a Changeset</h2>
          230  +
          231  +<p> The example code below demonstrates the steps involved in capturing a
          232  +changeset while executing SQL commands. In summary:
          233  +
          234  +<ol>
          235  +  <li> <p>An session object (type sqlite3_session*) is created by making a 
          236  +          call to the [sqlite3session_create()] API function.
          237  +
          238  +       <p>A single session object monitors changes made to a single database 
          239  +          (i.e. "main", "temp" or an attached database) via a single 
          240  +          sqlite3* database handle.
          241  +
          242  +  <li> <p>The session object is configured with a set of tables to monitor
          243  +          changes on.
          244  +
          245  +       <p> By default a session object does not monitor changes on any 
          246  +           database table. Before it does so it must be configured. There 
          247  +           are three ways to configure the set of tables to monitor changes
          248  +           on:
          249  +       <ul>
          250  +         <li> By explicitly specifying tables using one call to
          251  +              [sqlite3session_attach()] for each table, or
          252  +
          253  +         <li> By specifying that all tables in the database should be monitored
          254  +              for changes using a call to [sqlite3session_attach()] with a
          255  +              NULL argument, or
          256  +
          257  +         <li> By configuring a callback to be invoked the first time each table
          258  +              is written to that indicates to the session module whether or
          259  +              not changes on the table should be monitored.
          260  +       </ul>
          261  +        <p> The example code below uses the second of the methods enumerated
          262  +            above - it monitors for changes on all database tables.
          263  +
          264  +  <li> <p> Changes are made to the database by executing SQL statements. The
          265  +           session object records these changes.
          266  +
          267  +  <li> <p> A changeset blob is extracted from the session object using a call
          268  +           to [sqlite3session_changeset()] (or, if using patchsets, a call to
          269  +           the [sqlite3session_patchset()] function).
          270  +
          271  +  <li> <p> The session object is deleted using a call to the 
          272  +           [sqlite3session_delete()] API function.
          273  +
          274  +       <p> It is not necessary to delete a session object after extracting
          275  +           a changeset or patchset from it. It can be left attached to the
          276  +           database handle and will continue monitoring for changes on the
          277  +           configured tables as before. However, not that if 
          278  +           [sqlite3session_changeset()] or [sqlite3session_patchset()] is
          279  +           called a second time on a session ojbect the changeset or patchset
          280  +           still reflects all changes that have taken place on the connection
          281  +           since the session was created. A session object is not reset or
          282  +           zeroed by a call to sqlite3session_changeset() or patchset().
          283  +</ol>
          284  +
          285  +<codeblock>
          286  +  /*
          287  +  ** Argument zSql points to a buffer containing an SQL script to execute 
          288  +  ** against the database handle passed as the first argument. As well as
          289  +  ** executing the SQL script, this function collects a changeset recording
          290  +  ** all changes made to the "main" database file. Assuming no error occurs,
          291  +  ** output variables (*ppChangeset) and (*pnChangeset) are set to point
          292  +  ** to a buffer containing the changeset and the size of the changeset in
          293  +  ** bytes before returning SQLITE_OK. In this case it is the responsibility
          294  +  ** of the caller to eventually free the changeset blob by passing it to
          295  +  ** the sqlite3_free function.
          296  +  **
          297  +  ** Or, if an error does occur, return an SQLite error code. The final
          298  +  ** value of (*pChangeset) and (*pnChangeset) are undefined in this case.
          299  +  */
          300  +  int sql_exec_changeset(
          301  +    sqlite3 *db,                  /* Database handle */
          302  +    const char *zSql,             /* SQL script to execute */
          303  +    int *pnChangeset,             /* OUT: Size of changeset blob in bytes */
          304  +    void **ppChangeset            /* OUT: Pointer to changeset blob */
          305  +  ){
          306  +    sqlite3_session *pSession = 0;
          307  +    int rc;
          308  +
          309  +    /* Create a new session object */
          310  +    rc = sqlite3session_create(db, "main", &pSession);
          311  +
          312  +    /* Configure the session object to record changes to all tables */
          313  +    if( rc==SQLITE_OK ) rc = sqlite3session_attach(pSession, NULL);
          314  +
          315  +    /* Execute the SQL script */
          316  +    if( rc==SQLITE_OK ) rc = sqlite3_exec(db, zSql, 0, 0, 0);
          317  +
          318  +    /* Collect the changeset */
          319  +    if( rc==SQLITE_OK ){
          320  +      rc = sqlite3session_changeset(pSession, pnChangeset, ppChangeset);
          321  +    }
          322  +
          323  +    /* Delete the session object */
          324  +    sqlite3session_delete(pSession);
          325  +
          326  +    return rc;
          327  +  }
          328  +</codeblock>
          329  +
          330  +<h2>Applying a Changeset to a Database</h2>
          331  +
          332  +<p> Applying a changeset to a database is simpler than capturing a changeset.
          333  +Usually, a single call to [sqlite3changeset_apply()], as depicted in the
          334  +example code below, suffices.
          335  +
          336  +<p> In cases where it is complicated, the complications in applying a 
          337  +changeset lie in conflict resolution. Refer to the API documentation linked
          338  +above for details.
          339  +
          340  +  <codeblock>
          341  +    /*
          342  +    ** Conflict handler callback used by apply_changeset(). See below.
          343  +    */
          344  +    static int xConflict(void *pCtx, int eConflict, sqlite3_changset_iter *pIter){
          345  +      int ret = (int)pCtx;
          346  +      return ret;
          347  +    }
          348  +
          349  +    /*
          350  +    ** Apply the changeset contained in blob pChangeset, size nChangeset bytes,
          351  +    ** to the main database of the database handle passed as the first argument.
          352  +    ** Return SQLITE_OK if successful, or an SQLite error code if an error
          353  +    ** occurs.
          354  +    **
          355  +    ** If parameter bIgnoreConflicts is true, then any conflicting changes 
          356  +    ** within the changeset are simply ignored. Or, if bIgnoreConflicts is
          357  +    ** false, then this call fails with an SQLTIE_ABORT error if a changeset
          358  +    ** conflict is encountered.
          359  +    */
          360  +    int apply_changeset(
          361  +      sqlite3 *db,                  /* Database handle */
          362  +      int bIgnoreConflicts,         /* True to ignore conflicting changes */
          363  +      int nChangeset,               /* Size of changeset in bytes */
          364  +      void *pChangeset              /* Pointer to changeset blob */
          365  +    ){
          366  +      return sqlite3changeset_apply(
          367  +          db, 
          368  +          nChangeset, pChangeset, 
          369  +          0, xConflict, 
          370  +          (void*)bIgnoreConflicts
          371  +      );
          372  +    }
          373  +</codeblock>
          374  +
          375  +<h2>Inspecting the Contents of a Changeset</h2>
          376  +
          377  +<p> The example code below demonstrates the techniques used to iterate 
          378  +through and extract the data related to all changes in a changeset. To
          379  +summarize:
          380  +
          381  +<ol>
          382  +  <li><p> The [sqlite3changeset_start()] API is called to create and
          383  +          initialize an iterator to iterate through the contents of a
          384  +          changeset. Initially, the iterator points to no element at all.
          385  +
          386  +  <li><p> The first call to [sqlite3changeset_next()] on the iterator moves
          387  +          it to point to the first change  in the changeset (or to EOF, if 
          388  +          the changeset is completely empty). sqlite3changeset_next() returns
          389  +          SQLITE_ROW if it moves the iterator to point to a valid entry,
          390  +          SQLITE_DONE if it moves the iterator to EOF, or an SQLite error
          391  +          code if an error occurs.
          392  +
          393  +  <li><p> If the iterator points to a valid entry, the [sqlite3changeset_op()]
          394  +          API may be used to determine the type of change (INSERT, UPDATE or
          395  +          DELETE) that the iterator points to. Additionally, the same API 
          396  +          can be used to obtain the name of the table the change applies to
          397  +          and its expected number of columns and primary key columns.
          398  +
          399  +  <li><p> If the iterator points to a valid INSERT or UPDATE entry, the
          400  +          [sqlite3changeset_new()] API may be used to obtain the new.* values
          401  +          within the change payload.
          402  +
          403  +  <li><p> If the iterator points to a valid DELETE or UPDATE entry, the
          404  +          [sqlite3changeset_old()] API may be used to obtain the old.* values
          405  +          within the change payload.
          406  +
          407  +  <li><p> An iterator is deleted using a call to the 
          408  +          [sqlite3changeset_finalize()] API. If an error occured while
          409  +          iterating, an SQLite error code is returned (even if the same error
          410  +          code has already been returned by sqlite3changeset_next()). Or,
          411  +          if no error has occurred, SQLITE_OK is returned.
          412  +</ol>
          413  +
          414  +  <codeblock>
          415  +    /*
          416  +    ** Print the contents of the changeset to stdout.
          417  +    */
          418  +    static int print_changeset(void *pChangeset, int nChangeset){
          419  +      int rc;
          420  +      sqlite3_changeset_iter *pIter = 0;
          421  +
          422  +      /* Create an iterator to iterate through the changeset */
          423  +      rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
          424  +      if( rc!=SQLITE_OK ) return rc;
          425  +
          426  +      /* This loop runs once for each change in the changeset */
          427  +      while( SQLITE_ROW==sqlite3changeset_next(pIter) ){
          428  +        const char *zTab;           /* Table change applies to */
          429  +        int nCol;                   /* Number of columns in table zTab */
          430  +        int op;                     /* SQLITE_INSERT, UPDATE or DELETE */
          431  +        sqlite3_value *pVal;
          432  +
          433  +        /* Print the type of operation and the table it is on */
          434  +        rc = sqlite3changeset_op(pIter, &zTab, &nCol, &op, 0);
          435  +        if( rc!=SQLITE_OK ) goto exit_print_changeset;
          436  +        printf("%s on table %s\n",
          437  +          op==SQLITE_INSERT?"INSERT" : op==SQLITE_UPDATE?"UPDATE" : "DELETE",
          438  +          zTab
          439  +        );
          440  +
          441  +        /* If this is an UPDATE or DELETE, print the old.* values */
          442  +        if( op==SQLITE_UPDATE || op==SQLITE_DELETE ){
          443  +          printf("Old values:");
          444  +          for(i=0; i&lt;nCol; i++){
          445  +            rc = sqlite3changeset_old(pIter, i, &pVal);
          446  +            if( rc!=SQLITE_OK ) goto exit_print_changeset;
          447  +            printf(" %s", pVal ? sqlite3_value_text(pVal) : "-");
          448  +          }
          449  +          printf("\n");
          450  +        }
          451  +
          452  +        /* If this is an UPDATE or INSERT, print the new.* values */
          453  +        if( op==SQLITE_UPDATE || op==SQLITE_INSERT ){
          454  +          printf("New values:");
          455  +          for(i=0; i&lt;nCol; i++){
          456  +            rc = sqlite3changeset_new(pIter, i, &pVal);
          457  +            if( rc!=SQLITE_OK ) goto exit_print_changeset;
          458  +            printf(" %s", pVal ? sqlite3_value_text(pVal) : "-");
          459  +          }
          460  +          printf("\n");
          461  +        }
          462  +      }
          463  +
          464  +      /* Clean up the changeset and return an error code (or SQLITE_OK) */
          465  +     exit_print_changeset:
          466  +      rc2 = sqlite3changeset_finalize(pIter);
          467  +      if( rc==SQLITE_OK ) rc = rc2;
          468  +      return rc;
          469  +    }
          470  +</codeblock>
          471  +
          472  +<h1>Extended Functionality</h1>
          473  +
          474  +<p> Most applications will only use the session module functionality described
          475  +in the previous section. However, the following additional functionality is
          476  +available for the use and manipulation of changeset and patchset blobs:
          477  +
          478  +<ul>
          479  +  <li> <p>Two or more changeset/patchsets may be combined using the 
          480  +       [sqlite3changeset_concat()] or [sqlite3_changegroup] interfaces.
          481  +
          482  +  <li> <p>A changeset may be "inverted" using the [sqlite3changeset_invert()]
          483  +       API function. An inverted changeset undoes the changes made by the
          484  +       original. If changeset C<sup>+</sup> is the inverse of changeset C, then
          485  +       applying C and then C<sup>+</sup> to a database should leave
          486  +       the database unchanged.
          487  +
    61    488   
    62         -<i>TBD...</i>
          489  +

Changes to search/buildsearchdb.tcl.

    19     19   
    20     20       c3ref {
    21     21         set blacklist(objlist.html) 1
    22     22         set blacklist(constlist.html) 1
    23     23         set blacklist(funclist.html) 1
    24     24   
    25     25         lappend lFiles c3ref/free.html
           26  +      lappend lFiles c3ref/exec.html
    26     27         lappend lFiles c3ref/mprintf.html
    27     28         lappend lFiles c3ref/io_methods.html
    28     29   
    29     30         foreach f [glob c3ref/*.html] { 
    30     31           if {[info exists blacklist([file tail $f])]} continue
    31     32           if {[lsearch $lFiles $f]<0} { lappend lFiles $f }
    32     33         }