Skip to content

Commit db78644

Browse files
committed
fk_conflicts is only valid field in SQLITE_CHANGESET_FOREIGN_KEY
Pointers to underlying issue in #591.
1 parent 5b23d0f commit db78644

3 files changed

Lines changed: 71 additions & 28 deletions

File tree

apsw/tests/sessiontests.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -719,8 +719,19 @@ def handler(reason, tc):
719719

720720
def handler(reason, tc):
721721
self.assertEqual(reason, apsw.SQLITE_CHANGESET_FOREIGN_KEY)
722-
self.assertEqual(tc.op, "Undocumented op 0")
722+
self.assertEqual(tc.op, None)
723723
self.assertEqual(tc.fk_conflicts, 1)
724+
self.assertIn("SQLITE_CHANGESET_FOREIGN_KEY", str(tc))
725+
self.assertNotIn("op", str(tc))
726+
self.assertIsNone(tc.column_count)
727+
self.assertIsNone(tc.conflict)
728+
self.assertIsNone(tc.indirect)
729+
self.assertIsNone(tc.name)
730+
self.assertIsNone(tc.new)
731+
self.assertIsNone(tc.old)
732+
self.assertIsNone(tc.op)
733+
self.assertIsNone(tc.opcode)
734+
self.assertIsNone(tc.pk_columns)
724735
return apsw.SQLITE_CHANGESET_OMIT
725736

726737
apsw.Changeset.apply(changeset, self.db, filter=lambda n: n == "delete", conflict=handler)

doc/changes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ Take advantage of :code:`SQLITE_UTF8_ZT` encoding and
8686
`sqlite3_carray_bind_v2
8787
<https://sqlite.org/draft/c3ref/carray_bind.html>`__ in the C code.
8888

89+
A :class:`TableChange` for `SQLITE_CHANGESET_FOREIGN_KEY
90+
<https://sqlite.org/session.html#SQLITE_CHANGESET_CONFLICT>`__ returns
91+
:code:`None` for all fields except :attr:`~TableChange.fk_conflicts`
92+
8993
3.51.2.0
9094
========
9195

src/session.c

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,10 @@ APSWSession_tp_repr(PyObject *self_)
10381038
access fields when out of scope. This means you can't save
10391039
TableChanges for later, and need to copy out any information you need.
10401040
1041+
When a conflict handler is :code:`SQLITE_CHANGESET_FOREIGN_KEY` then
1042+
only the :attr:`fk_conflicts` field has information, and all the rest
1043+
will be :code:`None`.
1044+
10411045
*/
10421046

10431047
#define CHECK_TABLE_SCOPE \
@@ -1082,7 +1086,7 @@ APSWTableChange_name(PyObject *self_, void *Py_UNUSED(unused))
10821086
APSWTableChange *self = (APSWTableChange *)self_;
10831087
CHECK_TABLE_SCOPE;
10841088

1085-
return PyUnicode_FromString(self->table_name);
1089+
return self->operation ? PyUnicode_FromString(self->table_name) : Py_NewRef(Py_None);
10861090
}
10871091

10881092
/** .. attribute:: column_count
@@ -1096,7 +1100,7 @@ APSWTableChange_column_count(PyObject *self_, void *Py_UNUSED(unused))
10961100
APSWTableChange *self = (APSWTableChange *)self_;
10971101
CHECK_TABLE_SCOPE;
10981102

1099-
return PyLong_FromLong(self->table_column_count);
1103+
return self->operation ? PyLong_FromLong(self->table_column_count) : Py_NewRef(Py_None);
11001104
}
11011105

11021106
/** .. attribute:: opcode
@@ -1113,7 +1117,7 @@ APSWTableChange_opcode(PyObject *self_, void *Py_UNUSED(unused))
11131117
APSWTableChange *self = (APSWTableChange *)self_;
11141118
CHECK_TABLE_SCOPE;
11151119

1116-
return PyLong_FromLong(self->operation);
1120+
return self->operation ? PyLong_FromLong(self->operation) : Py_NewRef(Py_None);
11171121
}
11181122

11191123
/** .. attribute:: op
@@ -1135,8 +1139,8 @@ APSWTableChange_op(PyObject *self_, void *Py_UNUSED(unused))
11351139
return Py_NewRef(apst.DELETE);
11361140
if (self->operation == SQLITE_UPDATE)
11371141
return Py_NewRef(apst.UPDATE);
1138-
/* https://sqlite.org/forum/forumpost/09c94dfb08 */
1139-
return PyUnicode_FromFormat("Undocumented op %d", self->operation);
1142+
1143+
Py_RETURN_NONE;
11401144
}
11411145

11421146
/** .. attribute:: indirect
@@ -1150,6 +1154,9 @@ APSWTableChange_indirect(PyObject *self_, void *Py_UNUSED(unused))
11501154
{
11511155
APSWTableChange *self = (APSWTableChange *)self_;
11521156
CHECK_TABLE_SCOPE;
1157+
1158+
if (!self->operation)
1159+
Py_RETURN_NONE;
11531160
if (self->indirect)
11541161
Py_RETURN_TRUE;
11551162

@@ -1171,6 +1178,9 @@ APSWTableChange_new(PyObject *self_, void *Py_UNUSED(unused))
11711178
APSWTableChange *self = (APSWTableChange *)self_;
11721179
CHECK_TABLE_SCOPE;
11731180

1181+
if (!self->operation)
1182+
Py_RETURN_NONE;
1183+
11741184
sqlite3_value *value, *misuse_check;
11751185
if (SQLITE_MISUSE == sqlite3changeset_new(self->iter, 0, &misuse_check))
11761186
Py_RETURN_NONE;
@@ -1220,6 +1230,9 @@ APSWTableChange_old(PyObject *self_, void *Py_UNUSED(unused))
12201230
APSWTableChange *self = (APSWTableChange *)self_;
12211231
CHECK_TABLE_SCOPE;
12221232

1233+
if (!self->operation)
1234+
Py_RETURN_NONE;
1235+
12231236
sqlite3_value *value, *misuse_check;
12241237
if (SQLITE_MISUSE == sqlite3changeset_old(self->iter, 0, &misuse_check))
12251238
Py_RETURN_NONE;
@@ -1267,6 +1280,10 @@ APSWTableChange_conflict(PyObject *self_, void *Py_UNUSED(unused))
12671280
{
12681281
APSWTableChange *self = (APSWTableChange *)self_;
12691282
CHECK_TABLE_SCOPE;
1283+
1284+
if (!self->operation)
1285+
Py_RETURN_NONE;
1286+
12701287
sqlite3_value *value;
12711288
int res = sqlite3changeset_conflict(self->iter, 0, &value);
12721289
if (res == SQLITE_MISUSE)
@@ -1344,6 +1361,9 @@ APSWTableChange_pk_columns(PyObject *self_, void *Py_UNUSED(unused))
13441361
APSWTableChange *self = (APSWTableChange *)self_;
13451362
CHECK_TABLE_SCOPE;
13461363

1364+
if (!self->operation)
1365+
Py_RETURN_NONE;
1366+
13471367
unsigned char *abPK;
13481368
int nCol;
13491369

@@ -1387,29 +1407,37 @@ APSWTableChange_tp_repr(PyObject *self_)
13871407
if (!self->iter)
13881408
return PyUnicode_FromFormat("<%s out of scope, at %p>", Py_TypeName(self_), self);
13891409

1390-
PyObject *op = NULL, *old = NULL, *new_vals = NULL, *conflict = NULL, *pk_columns = NULL, *fk_conflicts = NULL;
1391-
1392-
op = APSWTableChange_op(self_, NULL);
1393-
if (op)
1394-
old = APSWTableChange_old(self_, NULL);
1395-
if (old)
1396-
new_vals = APSWTableChange_new(self_, NULL);
1397-
if (new_vals)
1398-
conflict = APSWTableChange_conflict(self_, NULL);
1399-
if (conflict)
1400-
pk_columns = APSWTableChange_pk_columns(self_, NULL);
1401-
if (pk_columns)
1402-
fk_conflicts = APSWTableChange_fk_conflicts(self_, NULL);
1403-
1404-
PyObject *res = NULL;
1405-
1406-
if (fk_conflicts)
1407-
res = PyUnicode_FromFormat("<%s name=\"%s\", column_count=%d, pk_columns=%S, operation=%U, "
1408-
"indirect=%S, old=%S, new=%S, conflict=%S, fk_conflicts=%S, at %p>",
1409-
Py_TypeName(self_), self->table_name ? self->table_name : "(NULL)",
1410-
self->table_column_count, pk_columns, op, (self->indirect) ? Py_True : Py_False, old,
1411-
new_vals, conflict, fk_conflicts, self);
1410+
PyObject *op = NULL, *old = NULL, *new_vals = NULL, *conflict = NULL, *pk_columns = NULL, *fk_conflicts = NULL,
1411+
*res = NULL;
14121412

1413+
if (!self->operation)
1414+
{
1415+
fk_conflicts = APSWTableChange_fk_conflicts(self_, NULL);
1416+
if (fk_conflicts)
1417+
res = PyUnicode_FromFormat("<%s SQLITE_CHANGESET_FOREIGN_KEY fk_conflicts=%S, at %p>", Py_TypeName(self_),
1418+
fk_conflicts, self);
1419+
}
1420+
else
1421+
{
1422+
op = APSWTableChange_op(self_, NULL);
1423+
if (op)
1424+
old = APSWTableChange_old(self_, NULL);
1425+
if (old)
1426+
new_vals = APSWTableChange_new(self_, NULL);
1427+
if (new_vals)
1428+
conflict = APSWTableChange_conflict(self_, NULL);
1429+
if (conflict)
1430+
pk_columns = APSWTableChange_pk_columns(self_, NULL);
1431+
if (pk_columns)
1432+
fk_conflicts = APSWTableChange_fk_conflicts(self_, NULL);
1433+
1434+
if (fk_conflicts)
1435+
res = PyUnicode_FromFormat("<%s name=\"%s\", column_count=%d, pk_columns=%S, operation=%U, "
1436+
"indirect=%S, old=%S, new=%S, conflict=%S, fk_conflicts=%S, at %p>",
1437+
Py_TypeName(self_), self->table_name ? self->table_name : "(NULL)",
1438+
self->table_column_count, pk_columns, op, (self->indirect) ? Py_True : Py_False, old,
1439+
new_vals, conflict, fk_conflicts, self);
1440+
}
14131441
Py_XDECREF(op);
14141442
Py_XDECREF(old);
14151443
Py_XDECREF(new_vals);

0 commit comments

Comments
 (0)