summaryrefslogtreecommitdiffstats
path: root/www/session/sqlite3changeset_apply.html
blob: 02849ff6d559a6c3eb5232a8ac3c4951a40ac0a3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
<!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>Apply A Changeset To A Database</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>
<a href="../session/intro.html"><h2>Session Module C Interface</h2></a><h2>Apply A Changeset To A Database</h2><blockquote><pre>int sqlite3changeset_apply(
  sqlite3 *db,                    /* Apply change to "main" db of this handle */
  int nChangeset,                 /* Size of changeset in bytes */
  void *pChangeset,               /* Changeset blob */
  int(*xFilter)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    const char *zTab              /* Table name */
  ),
  int(*xConflict)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    int eConflict,                /* DATA, MISSING, CONFLICT, CONSTRAINT */
    sqlite3_changeset_iter *p     /* Handle describing change and conflict */
  ),
  void *pCtx                      /* First argument passed to xConflict */
);
int sqlite3changeset_apply_v2(
  sqlite3 *db,                    /* Apply change to "main" db of this handle */
  int nChangeset,                 /* Size of changeset in bytes */
  void *pChangeset,               /* Changeset blob */
  int(*xFilter)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    const char *zTab              /* Table name */
  ),
  int(*xConflict)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    int eConflict,                /* DATA, MISSING, CONFLICT, CONSTRAINT */
    sqlite3_changeset_iter *p     /* Handle describing change and conflict */
  ),
  void *pCtx,                     /* First argument passed to xConflict */
  void **ppRebase, int *pnRebase, /* OUT: Rebase data */
  int flags                       /* SESSION_CHANGESETAPPLY_* flags */
);
</pre></blockquote><p>
Apply a changeset or patchset to a database. These functions attempt to
update the "main" database attached to handle db with the changes found in
the changeset passed via the second and third arguments. </p>

<p>The fourth argument (xFilter) passed to these functions is the "filter
callback". If it is not NULL, then for each table affected by at least one
change in the changeset, the filter callback is invoked with
the table name as the second argument, and a copy of the context pointer
passed as the sixth argument as the first. If the "filter callback"
returns zero, then no attempt is made to apply any changes to the table.
Otherwise, if the return value is non-zero or the xFilter argument to
is NULL, all changes related to the table are attempted.</p>

<p>For each table that is not excluded by the filter callback, this function 
tests that the target database contains a compatible table. A table is 
considered compatible if all of the following are true:</p>

<p><ul>
  <li> The table has the same name as the name recorded in the 
       changeset, and
  <li> The table has at least as many columns as recorded in the 
       changeset, and
  <li> The table has primary key columns in the same position as 
       recorded in the changeset.
</ul></p>

<p>If there is no compatible table, it is not an error, but none of the
changes associated with the table are applied. A warning message is issued
via the sqlite3_log() mechanism with the error code SQLITE_SCHEMA. At most
one such warning is issued for each table in the changeset.</p>

<p>For each change for which there is a compatible table, an attempt is made 
to modify the table contents according to the UPDATE, INSERT or DELETE 
change. If a change cannot be applied cleanly, the conflict handler 
function passed as the fifth argument to sqlite3changeset_apply() may be 
invoked. A description of exactly when the conflict handler is invoked for 
each type of change is below.</p>

<p>Unlike the xFilter argument, xConflict may not be passed NULL. The results
of passing anything other than a valid function pointer as the xConflict
argument are undefined.</p>

<p>Each time the conflict handler function is invoked, it must return one
of <a href="../session/c_changeset_abort.html">SQLITE_CHANGESET_OMIT</a>, <a href="../session/c_changeset_abort.html">SQLITE_CHANGESET_ABORT</a> or 
<a href="../session/c_changeset_abort.html">SQLITE_CHANGESET_REPLACE</a>. SQLITE_CHANGESET_REPLACE may only be returned
if the second argument passed to the conflict handler is either
SQLITE_CHANGESET_DATA or SQLITE_CHANGESET_CONFLICT. If the conflict-handler
returns an illegal value, any changes already made are rolled back and
the call to sqlite3changeset_apply() returns SQLITE_MISUSE. Different 
actions are taken by sqlite3changeset_apply() depending on the value
returned by each invocation of the conflict-handler function. Refer to
the documentation for the three 
<a href="../session/c_changeset_abort.html">available return values</a> for details.</p>

<p><dl>
<dt>DELETE Changes<dd>
  For each DELETE change, the function checks if the target database 
  contains a row with the same primary key value (or values) as the 
  original row values stored in the changeset. If it does, and the values 
  stored in all non-primary key columns also match the values stored in 
  the changeset the row is deleted from the target database.</p>

<p>  If a row with matching primary key values is found, but one or more of
  the non-primary key fields contains a value different from the original
  row value stored in the changeset, the conflict-handler function is
  invoked with <a href="../session/c_changeset_conflict.html">SQLITE_CHANGESET_DATA</a> as the second argument. If the
  database table has more columns than are recorded in the changeset,
  only the values of those non-primary key fields are compared against
  the current database contents - any trailing database table columns
  are ignored.</p>

<p>  If no row with matching primary key values is found in the database,
  the conflict-handler function is invoked with <a href="../session/c_changeset_conflict.html">SQLITE_CHANGESET_NOTFOUND</a>
  passed as the second argument.</p>

<p>  If the DELETE operation is attempted, but SQLite returns SQLITE_CONSTRAINT
  (which can only happen if a foreign key constraint is violated), the
  conflict-handler function is invoked with <a href="../session/c_changeset_conflict.html">SQLITE_CHANGESET_CONSTRAINT</a>
  passed as the second argument. This includes the case where the DELETE
  operation is attempted because an earlier call to the conflict handler
  function returned <a href="../session/c_changeset_abort.html">SQLITE_CHANGESET_REPLACE</a>.</p>

<p><dt>INSERT Changes<dd>
  For each INSERT change, an attempt is made to insert the new row into
  the database. If the changeset row contains fewer fields than the
  database table, the trailing fields are populated with their default
  values.</p>

<p>  If the attempt to insert the row fails because the database already 
  contains a row with the same primary key values, the conflict handler
  function is invoked with the second argument set to 
  <a href="../session/c_changeset_conflict.html">SQLITE_CHANGESET_CONFLICT</a>.</p>

<p>  If the attempt to insert the row fails because of some other constraint
  violation (e.g. NOT NULL or UNIQUE), the conflict handler function is 
  invoked with the second argument set to <a href="../session/c_changeset_conflict.html">SQLITE_CHANGESET_CONSTRAINT</a>.
  This includes the case where the INSERT operation is re-attempted because 
  an earlier call to the conflict handler function returned 
  <a href="../session/c_changeset_abort.html">SQLITE_CHANGESET_REPLACE</a>.</p>

<p><dt>UPDATE Changes<dd>
  For each UPDATE change, the function checks if the target database 
  contains a row with the same primary key value (or values) as the 
  original row values stored in the changeset. If it does, and the values 
  stored in all modified non-primary key columns also match the values
  stored in the changeset the row is updated within the target database.</p>

<p>  If a row with matching primary key values is found, but one or more of
  the modified non-primary key fields contains a value different from an
  original row value stored in the changeset, the conflict-handler function
  is invoked with <a href="../session/c_changeset_conflict.html">SQLITE_CHANGESET_DATA</a> as the second argument. Since
  UPDATE changes only contain values for non-primary key fields that are
  to be modified, only those fields need to match the original values to
  avoid the SQLITE_CHANGESET_DATA conflict-handler callback.</p>

<p>  If no row with matching primary key values is found in the database,
  the conflict-handler function is invoked with <a href="../session/c_changeset_conflict.html">SQLITE_CHANGESET_NOTFOUND</a>
  passed as the second argument.</p>

<p>  If the UPDATE operation is attempted, but SQLite returns 
  SQLITE_CONSTRAINT, the conflict-handler function is invoked with 
  <a href="../session/c_changeset_conflict.html">SQLITE_CHANGESET_CONSTRAINT</a> passed as the second argument.
  This includes the case where the UPDATE operation is attempted after 
  an earlier call to the conflict handler function returned
  <a href="../session/c_changeset_abort.html">SQLITE_CHANGESET_REPLACE</a>.  
</dl></p>

<p>It is safe to execute SQL statements, including those that write to the
table that the callback related to, from within the xConflict callback.
This can be used to further customize the application's conflict
resolution strategy.</p>

<p>All changes made by these functions are enclosed in a savepoint transaction.
If any other error (aside from a constraint failure when attempting to
write to the target database) occurs, then the savepoint transaction is
rolled back, restoring the target database to its original state, and an 
SQLite error code returned.</p>

<p>If the output parameters (ppRebase) and (pnRebase) are non-NULL and
the input is a changeset (not a patchset), then sqlite3changeset_apply_v2()
may set (*ppRebase) to point to a "rebase" that may be used with the 
sqlite3_rebaser APIs buffer before returning. In this case (*pnRebase)
is set to the size of the buffer in bytes. It is the responsibility of the
caller to eventually free any such buffer using sqlite3_free(). The buffer
is only allocated and populated if one or more conflicts were encountered
while applying the patchset. See comments surrounding the sqlite3_rebaser
APIs for further details.</p>

<p>The behavior of sqlite3changeset_apply_v2() and its streaming equivalent
may be modified by passing a combination of
<a href="../session/c_changesetapply_fknoaction.html">supported flags</a> as the 9th parameter.</p>

<p>Note that the sqlite3changeset_apply_v2() API is still <b>experimental</b>
and therefore subject to change.
</p><p>See also lists of
  <a href="../session/objlist.html">Objects</a>,
  <a href="../session/constlist.html">Constants</a>, and
  <a href="../session/funclist.html">Functions</a>.</p>