SQLite

Check-in [d7c0758120]
Login

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

Overview
Comment:Fix bug in sessions handling of FK constraints introduced by [e09a0c02] (released in 3.48.0). Bug was preventing a changeset containing FK violations from being applied even when the xConflict(CHANGESET_FOREIGN_KEY) returned OMIT.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: d7c07581203a0a88456588e49e51b40a8341b0e7121809f75be0ee882d91650f
User & Date: dan 2025-01-29 18:53:19.277
Context
2025-01-30
11:19
Fix build regression, introduced in [d2fe6b05f38d9d] (3.48.0), in which SQLITE_OMIT and SQLITE_ENABLE flags passed to configure via CFLAGS were not propagated to the OPT_FEATURE_FLAGS list. Reported in forum post 9801e54665afd728. (check-in: ec71d9dcd5 user: stephan tags: trunk)
2025-01-29
19:02
Fix bug in sessions handling of FK constraints introduced by [e09a0c02] (released in 3.48.0). Bug was preventing a changeset containing FK violations from being applied even when the xConflict(CHANGESET_FOREIGN_KEY) returned OMIT. (check-in: 946f33cd45 user: dan tags: branch-3.48)
18:53
Fix bug in sessions handling of FK constraints introduced by [e09a0c02] (released in 3.48.0). Bug was preventing a changeset containing FK violations from being applied even when the xConflict(CHANGESET_FOREIGN_KEY) returned OMIT. (check-in: d7c0758120 user: dan tags: trunk)
2025-01-28
20:32
Enhance the if() and iif() SQL functions so that they support any number of arguments greater than or equal to two. Suggested by forum post 40f7867f75f80. (check-in: fb76d184ee user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to ext/session/session1.test.
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
  DELETE FROM t2 WHERE a = 4;
} -conflicts {
  {DELETE t2 NOTFOUND {i 3 t three}}
  {DELETE t2 DATA {i 4 t four} {i 4 t five}}
  {FOREIGN_KEY 1}
}
do_execsql_test $tn.3.2.4 "SELECT * FROM t2" {}
do_db2_test $tn.3.2.5 "SELECT * FROM t2" {1 one 2 two 4 five}

# Test UPDATE changesets.
#
do_execsql_test $tn.3.3.1 {
  CREATE TABLE t4(a, b, c, PRIMARY KEY(b, c))%WR%;
  INSERT INTO t4 VALUES(1, 2, 3);
  INSERT INTO t4 VALUES(4, 5, 6);







|







281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
  DELETE FROM t2 WHERE a = 4;
} -conflicts {
  {DELETE t2 NOTFOUND {i 3 t three}}
  {DELETE t2 DATA {i 4 t four} {i 4 t five}}
  {FOREIGN_KEY 1}
}
do_execsql_test $tn.3.2.4 "SELECT * FROM t2" {}
do_db2_test $tn.3.2.5 "SELECT * FROM t2" {4 five}

# Test UPDATE changesets.
#
do_execsql_test $tn.3.3.1 {
  CREATE TABLE t4(a, b, c, PRIMARY KEY(b, c))%WR%;
  INSERT INTO t4 VALUES(1, 2, 3);
  INSERT INTO t4 VALUES(4, 5, 6);
Changes to ext/session/session9.test.
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
  4   3 1 {FOREIGN_KEY 1} OMIT
  5   2 0 {FOREIGN_KEY 1} ABORT
  6   3 0 {FOREIGN_KEY 1} ABORT
  7   2 1 {FOREIGN_KEY 1} ABORT
  8   3 1 {FOREIGN_KEY 1} ABORT
} {

  set A(OMIT,0)  {1 SQLITE_CONSTRAINT}
  set A(OMIT,1)  {0 {}}
  set A(ABORT,0) {1 SQLITE_CONSTRAINT}
  set A(ABORT,1) {1 SQLITE_CONSTRAINT}
  do_test 1.2.$tn.1 {
    populate_db
    execsql { DELETE FROM p1 WHERE a=($delrow+0) }
    if {$trans} { execsql BEGIN }

    set ::xConflict [list]
    list [catch {sqlite3changeset_apply db $::cc xConflict} msg] $msg
  } $A($conflictret,$trans)

  do_test 1.2.$tn.2 { set ::xConflict } $conflictargs

  set A(OMIT,0)  {0 0}
  set A(OMIT,1)  {1 1}
  set A(ABORT,0) {0 0}
  set A(ABORT,1) {0 0}

  do_test 1.2.$tn.3 {
    execsql { SELECT count(*) FROM c1 UNION ALL SELECT count(*) FROM c2 }
  } $A($conflictret,$trans)







|














|







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
  4   3 1 {FOREIGN_KEY 1} OMIT
  5   2 0 {FOREIGN_KEY 1} ABORT
  6   3 0 {FOREIGN_KEY 1} ABORT
  7   2 1 {FOREIGN_KEY 1} ABORT
  8   3 1 {FOREIGN_KEY 1} ABORT
} {

  set A(OMIT,0)  {0 {}}
  set A(OMIT,1)  {0 {}}
  set A(ABORT,0) {1 SQLITE_CONSTRAINT}
  set A(ABORT,1) {1 SQLITE_CONSTRAINT}
  do_test 1.2.$tn.1 {
    populate_db
    execsql { DELETE FROM p1 WHERE a=($delrow+0) }
    if {$trans} { execsql BEGIN }

    set ::xConflict [list]
    list [catch {sqlite3changeset_apply db $::cc xConflict} msg] $msg
  } $A($conflictret,$trans)

  do_test 1.2.$tn.2 { set ::xConflict } $conflictargs

  set A(OMIT,0)  {1 1}
  set A(OMIT,1)  {1 1}
  set A(ABORT,0) {0 0}
  set A(ABORT,1) {0 0}

  do_test 1.2.$tn.3 {
    execsql { SELECT count(*) FROM c1 UNION ALL SELECT count(*) FROM c2 }
  } $A($conflictret,$trans)
Changes to ext/session/sessionnoact.test.
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
do_execsql_test 1.2 {
  PRAGMA foreign_keys = 1;
}

set ::nConflict 0
proc conflict {args} {
  incr ::nConflict
  return "OMIT"
}

sqlite3changeset_apply_v2 db $C conflict

do_execsql_test 1.3 {
  SELECT * FROM c1
} {







|







55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
do_execsql_test 1.2 {
  PRAGMA foreign_keys = 1;
}

set ::nConflict 0
proc conflict {args} {
  incr ::nConflict
  return "ABORT"
}

sqlite3changeset_apply_v2 db $C conflict

do_execsql_test 1.3 {
  SELECT * FROM c1
} {
107
108
109
110
111
112
113



114
115
116
117
118
119
120
  PRAGMA foreign_key_check
}

#-------------------------------------------------------------------------
# Check that a changeset that causes an FK violation may not be applied,
# even if SQLITE_CHANGESETAPPLY_FKNOACTION is specified.
#



reset_db
do_execsql_test 2.0 {
  CREATE TABLE p1(a INTEGER PRIMARY KEY, b, c UNIQUE);
  INSERT INTO p1 VALUES(1, 1, 'one');
  INSERT INTO p1 VALUES(2, 2, 'two');

  CREATE TABLE c1(x REFERENCES p1(c) ON DELETE CASCADE);







>
>
>







107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
  PRAGMA foreign_key_check
}

#-------------------------------------------------------------------------
# Check that a changeset that causes an FK violation may not be applied,
# even if SQLITE_CHANGESETAPPLY_FKNOACTION is specified.
#
# UPDATE: Unless the conflict-handler returns OMIT. In that case it can
# be committed. See test cases 3.* in this file.
#
reset_db
do_execsql_test 2.0 {
  CREATE TABLE p1(a INTEGER PRIMARY KEY, b, c UNIQUE);
  INSERT INTO p1 VALUES(1, 1, 'one');
  INSERT INTO p1 VALUES(2, 2, 'two');

  CREATE TABLE c1(x REFERENCES p1(c) ON DELETE CASCADE);
159
160
161
162
163
164
165
166





























































167
168
} {1 SQLITE_CONSTRAINT}
do_execsql_test 2.7 {
  SELECT * FROM p1;
} {1 1 one 2 2 two}
do_execsql_test 2.8 {
  SELECT * FROM c1;
} {two}






























































finish_test









>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


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
} {1 SQLITE_CONSTRAINT}
do_execsql_test 2.7 {
  SELECT * FROM p1;
} {1 1 one 2 2 two}
do_execsql_test 2.8 {
  SELECT * FROM c1;
} {two}

#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 3.0 {
  CREATE TABLE p1(a INTEGER PRIMARY KEY, b, c UNIQUE);
  INSERT INTO p1 VALUES(1, 1, 'one');
  INSERT INTO p1 VALUES(2, 2, 'two');

  CREATE TABLE c1(x REFERENCES p1(c) ON DELETE CASCADE);
  INSERT INTO c1 VALUES('two');
}

set ::nConflict 0
proc conflict {args} {
  incr ::nConflict
  return "OMIT"
}

db_save

set C [changeset_from_sql {
  DELETE FROM p1 WHERE a=2;
}]

db_restore_and_reopen

do_test 3.1 {
  sqlite3changeset_apply_v2 -noaction db $C conflict
} {}
do_execsql_test 3.2 {
  SELECT * FROM p1
} {1 1 one}

db_restore_and_reopen
db eval { PRAGMA foreign_keys = 1 }

do_test 3.3 {
  list [catch { sqlite3changeset_apply_v2 -noaction db $C conflict } msg] $msg
} {0 {}}
do_execsql_test 3.4 {
  SELECT * FROM p1;
} {1 1 one}
do_execsql_test 3.5 {
  SELECT * FROM c1;
} {two}

db_restore_and_reopen
db eval { PRAGMA foreign_keys = 1 }

do_test 3.6 {
  list [catch { 
    sqlite3changeset_apply_v2 -ignorenoop -noaction db $C conflict 
  } msg] $msg
} {0 {}}
do_execsql_test 3.7 {
  SELECT * FROM p1;
} {1 1 one}
do_execsql_test 3.8 {
  SELECT * FROM c1;
} {two}

finish_test

Changes to ext/session/sqlite3session.c.
5313
5314
5315
5316
5317
5318
5319





5320
5321
5322
5323
5324
5325
5326
      sIter.nCol = nFk;
      res = xConflict(pCtx, SQLITE_CHANGESET_FOREIGN_KEY, &sIter);
      if( res!=SQLITE_CHANGESET_OMIT ){
        rc = SQLITE_CONSTRAINT;
      }
    }
  }






  if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){
    if( rc==SQLITE_OK ){
      rc = sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
    }
    if( rc!=SQLITE_OK ){
      sqlite3_exec(db, "ROLLBACK TO changeset_apply", 0, 0, 0);







>
>
>
>
>







5313
5314
5315
5316
5317
5318
5319
5320
5321
5322
5323
5324
5325
5326
5327
5328
5329
5330
5331
      sIter.nCol = nFk;
      res = xConflict(pCtx, SQLITE_CHANGESET_FOREIGN_KEY, &sIter);
      if( res!=SQLITE_CHANGESET_OMIT ){
        rc = SQLITE_CONSTRAINT;
      }
    }
  }

  {
    int rc2 = sqlite3_exec(db, "PRAGMA defer_foreign_keys = 0", 0, 0, 0);
    if( rc==SQLITE_OK ) rc = rc2;
  }

  if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){
    if( rc==SQLITE_OK ){
      rc = sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
    }
    if( rc!=SQLITE_OK ){
      sqlite3_exec(db, "ROLLBACK TO changeset_apply", 0, 0, 0);