Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Store primary key definitions for modified tables in changesets. Add the sqlite3changeset_pk() API to extract this data from a changeset iterator. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | sessions |
Files: | files | file ages | folders |
SHA1: |
54298ee5ed183d1f1c49524f25e8ae14 |
User & Date: | dan 2011-03-24 11:22:59.000 |
Context
2011-03-24
| ||
16:04 | Fix handling of schema mismatches in sqlite3session.c so that it matches the docs in sqlite3session.h. (check-in: 506a0d7a71 user: dan tags: sessions) | |
11:22 | Store primary key definitions for modified tables in changesets. Add the sqlite3changeset_pk() API to extract this data from a changeset iterator. (check-in: 54298ee5ed user: dan tags: sessions) | |
2011-03-23
| ||
22:48 | Merge in all the latest changes from the trunk, and especially the interface changes to the SystemCall methods of the VFS. (check-in: 9c3a6e4799 user: drh tags: sessions) | |
Changes
Changes to ext/session/session1.test.
︙ | ︙ | |||
84 85 86 87 88 89 90 | sqlite3session S db main S attach t1 execsql { INSERT INTO t1 VALUES(1, 'Sukhothai') } execsql { INSERT INTO t1 VALUES(2, 'Ayutthaya') } execsql { INSERT INTO t1 VALUES(3, 'Thonburi') } } {} do_changeset_test 2.1.2 S { | | | | | | | | | | | | | | | | | | | | | | | | 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 | sqlite3session S db main S attach t1 execsql { INSERT INTO t1 VALUES(1, 'Sukhothai') } execsql { INSERT INTO t1 VALUES(2, 'Ayutthaya') } execsql { INSERT INTO t1 VALUES(3, 'Thonburi') } } {} do_changeset_test 2.1.2 S { {INSERT t1 0 X. {} {i 1 t Sukhothai}} {INSERT t1 0 X. {} {i 2 t Ayutthaya}} {INSERT t1 0 X. {} {i 3 t Thonburi}} } do_changeset_invert_test 2.1.3 S { {DELETE t1 0 X. {i 1 t Sukhothai} {}} {DELETE t1 0 X. {i 2 t Ayutthaya} {}} {DELETE t1 0 X. {i 3 t Thonburi} {}} } do_test 2.1.4 { S delete } {} do_test 2.2.1 { sqlite3session S db main S attach t1 execsql { DELETE FROM t1 WHERE 1 } } {} do_changeset_test 2.2.2 S { {DELETE t1 0 X. {i 1 t Sukhothai} {}} {DELETE t1 0 X. {i 2 t Ayutthaya} {}} {DELETE t1 0 X. {i 3 t Thonburi} {}} } do_changeset_invert_test 2.2.3 S { {INSERT t1 0 X. {} {i 1 t Sukhothai}} {INSERT t1 0 X. {} {i 2 t Ayutthaya}} {INSERT t1 0 X. {} {i 3 t Thonburi}} } do_test 2.2.4 { S delete } {} do_test 2.3.1 { execsql { DELETE FROM t1 } sqlite3session S db main execsql { INSERT INTO t1 VALUES(1, 'Sukhothai') } execsql { INSERT INTO t1 VALUES(2, 'Ayutthaya') } execsql { INSERT INTO t1 VALUES(3, 'Thonburi') } S attach t1 execsql { UPDATE t1 SET x = 10 WHERE x = 1; UPDATE t1 SET y = 'Surin' WHERE x = 2; UPDATE t1 SET x = 20, y = 'Thapae' WHERE x = 3; } } {} do_changeset_test 2.3.2 S { {INSERT t1 0 X. {} {i 10 t Sukhothai}} {DELETE t1 0 X. {i 1 t Sukhothai} {}} {UPDATE t1 0 X. {i 2 t Ayutthaya} {{} {} t Surin}} {DELETE t1 0 X. {i 3 t Thonburi} {}} {INSERT t1 0 X. {} {i 20 t Thapae}} } do_changeset_invert_test 2.3.3 S { {DELETE t1 0 X. {i 10 t Sukhothai} {}} {INSERT t1 0 X. {} {i 1 t Sukhothai}} {UPDATE t1 0 X. {{} {} t Surin} {i 2 t Ayutthaya}} {INSERT t1 0 X. {} {i 3 t Thonburi}} {DELETE t1 0 X. {i 20 t Thapae} {}} } do_test 2.3.4 { S delete } {} do_test 2.4.1 { sqlite3session S db main S attach t1 execsql { INSERT INTO t1 VALUES(100, 'Bangkok') } |
︙ | ︙ |
Changes to ext/session/session2.test.
︙ | ︙ | |||
37 38 39 40 41 42 43 | CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES('i', 'one'); } do_iterator_test 1.1 t1 { DELETE FROM t1 WHERE a = 'i'; INSERT INTO t1 VALUES('ii', 'two'); } { | | | | | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES('i', 'one'); } do_iterator_test 1.1 t1 { DELETE FROM t1 WHERE a = 'i'; INSERT INTO t1 VALUES('ii', 'two'); } { {DELETE t1 0 X. {t i t one} {}} {INSERT t1 0 X. {} {t ii t two}} } do_iterator_test 1.2 t1 { INSERT INTO t1 VALUES(1.5, 99.9) } { {INSERT t1 0 X. {} {f 1.5 f 99.9}} } # Execute each of the following blocks of SQL on database [db1]. Collect # changes using a session object. Apply the resulting changeset to # database [db2]. Then check that the contents of the two databases are # identical. |
︙ | ︙ | |||
224 225 226 227 228 229 230 | foreach {tn sql changeset} { 1 { INSERT INTO t1 VALUES(123); INSERT INTO t1 VALUES(NULL); INSERT INTO t1 VALUES(456); } { | | | | | | | | | | | | | 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 | foreach {tn sql changeset} { 1 { INSERT INTO t1 VALUES(123); INSERT INTO t1 VALUES(NULL); INSERT INTO t1 VALUES(456); } { {INSERT t1 0 X {} {i 456}} {INSERT t1 0 X {} {i 123}} } 2 { UPDATE t1 SET a = NULL; } { {DELETE t1 0 X {i 456} {}} {DELETE t1 0 X {i 123} {}} } 3 { DELETE FROM t1 } { } 4 { INSERT INTO t3 VALUES(NULL, NULL) } { {INSERT t3 0 .X {} {n {} i 1}} } 5 { INSERT INTO t2 VALUES(1, 2, NULL) } { } 6 { INSERT INTO t2 VALUES(1, NULL, 3) } { } 7 { INSERT INTO t2 VALUES(1, NULL, NULL) } { } 8 { INSERT INTO t2 VALUES(1, 2, 3) } { {INSERT t2 0 .XX {} {i 1 i 2 i 3}} } 9 { DELETE FROM t2 WHERE 1 } { {DELETE t2 0 .XX {i 1 i 2 i 3} {}} } } { do_iterator_test 4.$tn {t1 t2 t3} $sql $changeset } #------------------------------------------------------------------------- # Test that if NULL is passed to sqlite3session_attach(), all database # tables are attached to the session object. # test_reset do_execsql_test 5.0 { CREATE TABLE t1(a PRIMARY KEY); CREATE TABLE t2(x, y PRIMARY KEY); } foreach {tn sql changeset} { 1 { INSERT INTO t1 VALUES(35) } { {INSERT t1 0 X {} {i 35}} } 2 { INSERT INTO t2 VALUES(36, 37) } { {INSERT t2 0 .X {} {i 36 i 37}} } 3 { DELETE FROM t1 WHERE 1; UPDATE t2 SET x = 34; } { {UPDATE t2 0 .X {i 36 i 37} {i 34 {} {}}} {DELETE t1 0 X {i 35} {}} } } { do_iterator_test 5.$tn * $sql $changeset } #------------------------------------------------------------------------- # The next block of tests verify that the "indirect" flag is set |
︙ | ︙ | |||
310 311 312 313 314 315 316 | do_iterator_test 6.1.1 * { INSERT INTO t1 VALUES(1, 'one', 'i'); SELECT indirect(1); INSERT INTO t1 VALUES(2, 'two', 'ii'); SELECT indirect(0); INSERT INTO t1 VALUES(3, 'three', 'iii'); } { | | | | | | | | | | | | | | 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 | do_iterator_test 6.1.1 * { INSERT INTO t1 VALUES(1, 'one', 'i'); SELECT indirect(1); INSERT INTO t1 VALUES(2, 'two', 'ii'); SELECT indirect(0); INSERT INTO t1 VALUES(3, 'three', 'iii'); } { {INSERT t1 0 X.. {} {i 1 t one t i}} {INSERT t1 1 X.. {} {i 2 t two t ii}} {INSERT t1 0 X.. {} {i 3 t three t iii}} } do_iterator_test 6.1.2 * { SELECT indirect(1); UPDATE t1 SET c = 'I' WHERE a = 1; SELECT indirect(0); } { {UPDATE t1 1 X.. {i 1 {} {} t i} {{} {} {} {} t I}} } do_iterator_test 6.1.3 * { SELECT indirect(1); UPDATE t1 SET c = '.' WHERE a = 1; SELECT indirect(0); UPDATE t1 SET c = 'o' WHERE a = 1; } { {UPDATE t1 0 X.. {i 1 {} {} t I} {{} {} {} {} t o}} } do_iterator_test 6.1.4 * { SELECT indirect(0); UPDATE t1 SET c = 'x' WHERE a = 1; SELECT indirect(1); UPDATE t1 SET c = 'i' WHERE a = 1; } { {UPDATE t1 0 X.. {i 1 {} {} t o} {{} {} {} {} t i}} } do_iterator_test 6.1.4 * { SELECT indirect(1); UPDATE t1 SET c = 'y' WHERE a = 1; SELECT indirect(1); UPDATE t1 SET c = 'I' WHERE a = 1; } { {UPDATE t1 1 X.. {i 1 {} {} t i} {{} {} {} {} t I}} } do_iterator_test 6.1.5 * { INSERT INTO t2 VALUES(1, 'x'); } { {INSERT t2 0 X. {} {i 1 t x}} {INSERT t2 1 X. {} {i 2 n {}}} } do_iterator_test 6.1.6 * { SELECT indirect(1); INSERT INTO t2 VALUES(3, 'x'); SELECT indirect(0); UPDATE t2 SET y = 'y' WHERE x>2; } { {INSERT t2 0 X. {} {i 3 t y}} {INSERT t2 0 X. {} {i 4 t y}} } do_iterator_test 6.1.7 * { SELECT indirect(1); DELETE FROM t2 WHERE x = 4; SELECT indirect(0); INSERT INTO t2 VALUES(4, 'new'); } { {UPDATE t2 0 X. {i 4 t y} {{} {} t new}} } finish_test |
Changes to ext/session/sessionfault.test.
︙ | ︙ | |||
239 240 241 242 243 244 245 | set {} {} } -test { faultsim_test_result {0 {}} {1 SQLITE_NOMEM} if {$testrc==0} { set x [list] sqlite3session_foreach c $::inverse { lappend x $c } foreach c { | | | | | 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 | set {} {} } -test { faultsim_test_result {0 {}} {1 SQLITE_NOMEM} if {$testrc==0} { set x [list] sqlite3session_foreach c $::inverse { lappend x $c } foreach c { {DELETE t1 0 .X {t xxx t yyy} {}} {INSERT t1 0 .X {} {t string i 1}} {UPDATE t1 0 .X {i 20 {} {}} {i 4 i 2}} } { lappend y $c } if {$x != $y} { error "changeset no good" } } } finish_test |
Changes to ext/session/sqlite3session.c.
︙ | ︙ | |||
35 36 37 38 39 40 41 42 43 44 45 46 47 48 | u8 *pNext; /* Pointer to next change within aChangeset */ int rc; /* Iterator error code */ sqlite3_stmt *pConflict; /* Points to conflicting row, if any */ char *zTab; /* Current table */ int nCol; /* Number of columns in zTab */ int op; /* Current operation */ int bIndirect; /* True if current change was indirect */ sqlite3_value **apValue; /* old.* and new.* values */ }; /* ** Each session object maintains a set of the following structures, one ** for each table the session object is monitoring. The structures are ** stored in a linked list starting at sqlite3_session.pTable. | > | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | u8 *pNext; /* Pointer to next change within aChangeset */ int rc; /* Iterator error code */ sqlite3_stmt *pConflict; /* Points to conflicting row, if any */ char *zTab; /* Current table */ int nCol; /* Number of columns in zTab */ int op; /* Current operation */ int bIndirect; /* True if current change was indirect */ u8 *abPK; /* Primary key array */ sqlite3_value **apValue; /* old.* and new.* values */ }; /* ** Each session object maintains a set of the following structures, one ** for each table the session object is monitoring. The structures are ** stored in a linked list starting at sqlite3_session.pTable. |
︙ | ︙ | |||
1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 | sqlite3_stmt *pSel = 0; /* SELECT statement to query table pTab */ int nRewind = buf.nBuf; /* Initial size of write buffer */ int nNoop; /* Size of buffer after writing tbl header */ /* Write a table header */ sessionAppendByte(&buf, 'T', &rc); sessionAppendVarint(&buf, nCol, &rc); sessionAppendBlob(&buf, (u8 *)zName, strlen(zName)+1, &rc); /* Build and compile a statement to execute: */ if( rc==SQLITE_OK ){ rc = sessionSelectStmt( db, pSession->zDb, zName, nCol, pTab->azCol, abPK, &pSel); } | > | 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 | sqlite3_stmt *pSel = 0; /* SELECT statement to query table pTab */ int nRewind = buf.nBuf; /* Initial size of write buffer */ int nNoop; /* Size of buffer after writing tbl header */ /* Write a table header */ sessionAppendByte(&buf, 'T', &rc); sessionAppendVarint(&buf, nCol, &rc); sessionAppendBlob(&buf, pTab->abPK, nCol, &rc); sessionAppendBlob(&buf, (u8 *)zName, strlen(zName)+1, &rc); /* Build and compile a statement to execute: */ if( rc==SQLITE_OK ){ rc = sessionSelectStmt( db, pSession->zDb, zName, nCol, pTab->azCol, abPK, &pSel); } |
︙ | ︙ | |||
1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 | } aChange = p->pNext; c = *(aChange++); if( c=='T' ){ int nByte; /* Bytes to allocate for apValue */ aChange += sessionVarintGet(aChange, &p->nCol); p->zTab = (char *)aChange; aChange += (strlen((char *)aChange) + 1); p->op = *(aChange++); p->bIndirect = *(aChange++); sqlite3_free(p->apValue); nByte = sizeof(sqlite3_value *) * p->nCol * 2; p->apValue = (sqlite3_value **)sqlite3_malloc(nByte); | > > | 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 | } aChange = p->pNext; c = *(aChange++); if( c=='T' ){ int nByte; /* Bytes to allocate for apValue */ aChange += sessionVarintGet(aChange, &p->nCol); p->abPK = (u8 *)aChange; aChange += p->nCol; p->zTab = (char *)aChange; aChange += (strlen((char *)aChange) + 1); p->op = *(aChange++); p->bIndirect = *(aChange++); sqlite3_free(p->apValue); nByte = sizeof(sqlite3_value *) * p->nCol * 2; p->apValue = (sqlite3_value **)sqlite3_malloc(nByte); |
︙ | ︙ | |||
1607 1608 1609 1610 1611 1612 1613 | } p->pNext = aChange; return SQLITE_ROW; } /* | | > > > > > > > > > > | 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 | } p->pNext = aChange; return SQLITE_ROW; } /* ** The following function extracts information on the current change ** from a changeset iterator. They may only be called after changeset_next() ** has returned SQLITE_ROW. */ int sqlite3changeset_op( sqlite3_changeset_iter *pIter, /* Iterator handle */ const char **pzTab, /* OUT: Pointer to table name */ int *pnCol, /* OUT: Number of columns in table */ int *pOp, /* OUT: SQLITE_INSERT, DELETE or UPDATE */ int *pbIndirect /* OUT: True if change is indirect */ ){ *pOp = pIter->op; *pnCol = pIter->nCol; *pzTab = pIter->zTab; if( pbIndirect ) *pbIndirect = pIter->bIndirect; return SQLITE_OK; } int sqlite3changeset_pk( sqlite3_changeset_iter *pIter, /* Iterator object */ unsigned char **pabPK, /* OUT: Array of boolean - true for PK cols */ int *pnCol /* OUT: Number of entries in output array */ ){ *pabPK = pIter->abPK; if( pnCol ) *pnCol = pIter->nCol; return SQLITE_OK; } /* ** This function may only be called while the iterator is pointing to an ** SQLITE_UPDATE or SQLITE_DELETE change (see sqlite3changeset_op()). ** Otherwise, SQLITE_MISUSE is returned. ** ** It sets *ppValue to point to an sqlite3_value structure containing the |
︙ | ︙ | |||
1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 | aIn = (u8 *)pChangeset; i = 0; while( i<nChangeset ){ u8 eType = aIn[i]; switch( eType ){ case 'T': { int nByte = 1 + sessionVarintGet(&aIn[i+1], &nCol); nByte += 1 + strlen((char *)&aIn[i+nByte]); memcpy(&aOut[i], &aIn[i], nByte); i += nByte; break; } case SQLITE_INSERT: | > > > > > > > > | 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 | aIn = (u8 *)pChangeset; i = 0; while( i<nChangeset ){ u8 eType = aIn[i]; switch( eType ){ case 'T': { /* A 'table' record consists of: ** ** * A constant 'T' character, ** * Number of columns in said table (a varint), ** * An array of nCol bytes (abPK), ** * A nul-terminated table name. */ int nByte = 1 + sessionVarintGet(&aIn[i+1], &nCol); nByte += nCol; nByte += 1 + strlen((char *)&aIn[i+nByte]); memcpy(&aOut[i], &aIn[i], nByte); i += nByte; break; } case SQLITE_INSERT: |
︙ | ︙ |
Changes to ext/session/sqlite3session.h.
︙ | ︙ | |||
336 337 338 339 340 341 342 343 344 345 346 347 348 349 | int sqlite3changeset_op( sqlite3_changeset_iter *pIter, /* Iterator object */ const char **pzTab, /* OUT: Pointer to table name */ int *pnCol, /* OUT: Number of columns in table */ int *pOp, /* OUT: SQLITE_INSERT, DELETE or UPDATE */ int *pbIndirect /* OUT: True for an 'indirect' change */ ); /* ** CAPI3REF: Obtain old.* Values From A Changeset Iterator ** ** The pIter argument passed to this function may either be an iterator ** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator ** created by [sqlite3changeset_start()]. In the latter case, the most recent | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 | int sqlite3changeset_op( sqlite3_changeset_iter *pIter, /* Iterator object */ const char **pzTab, /* OUT: Pointer to table name */ int *pnCol, /* OUT: Number of columns in table */ int *pOp, /* OUT: SQLITE_INSERT, DELETE or UPDATE */ int *pbIndirect /* OUT: True for an 'indirect' change */ ); /* ** CAPI3REF: Obtain The Primary Key Definition Of A Table ** ** For each modified table, a changeset includes the following: ** ** <ul> ** <li> The number of columns in the table, and ** <li> Which of those columns make up the tables PRIMARY KEY. ** </ul> ** ** This function is used to find which columns comprise the PRIMARY KEY of ** the table modified by the change that iterator pIter currently points to. ** If successful, *pabPK is set to point to an array of nCol entries, where ** nCol is the number of columns in the table. Elements of *pabPK are set to ** 0x01 if the corresponding column is part of the tables primary key, or ** 0x00 if it is not. ** ** If argumet pnCol is not NULL, then *pnCol is set to the number of columns ** in the table. ** ** If this function is called when the iterator does not point to a valid ** entry, SQLITE_MISUSE is returned and the output variables zeroed. Otherwise, ** SQLITE_OK is returned and the output variables populated as described ** above. */ int sqlite3changeset_pk( sqlite3_changeset_iter *pIter, /* Iterator object */ unsigned char **pabPK, /* OUT: Array of boolean - true for PK cols */ int *pnCol /* OUT: Number of entries in output array */ ); /* ** CAPI3REF: Obtain old.* Values From A Changeset Iterator ** ** The pIter argument passed to this function may either be an iterator ** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator ** created by [sqlite3changeset_start()]. In the latter case, the most recent |
︙ | ︙ |
Changes to ext/session/test_session.c.
︙ | ︙ | |||
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 | int nCol; /* Number of columns in table */ int op; /* SQLITE_INSERT, UPDATE or DELETE */ const char *zTab; /* Name of table change applies to */ Tcl_Obj *pVar; /* Tcl value to set $VARNAME to */ Tcl_Obj *pOld; /* Vector of old.* values */ Tcl_Obj *pNew; /* Vector of new.* values */ int bIndirect; sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect); pVar = Tcl_NewObj(); Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj( op==SQLITE_INSERT ? "INSERT" : op==SQLITE_UPDATE ? "UPDATE" : "DELETE", -1 )); Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zTab, -1)); Tcl_ListObjAppendElement(0, pVar, Tcl_NewBooleanObj(bIndirect)); pOld = Tcl_NewObj(); if( op!=SQLITE_INSERT ){ int i; for(i=0; i<nCol; i++){ sqlite3_value *pVal; sqlite3changeset_old(pIter, i, &pVal); | > > > > > > > > > > > > > > | 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 | int nCol; /* Number of columns in table */ int op; /* SQLITE_INSERT, UPDATE or DELETE */ const char *zTab; /* Name of table change applies to */ Tcl_Obj *pVar; /* Tcl value to set $VARNAME to */ Tcl_Obj *pOld; /* Vector of old.* values */ Tcl_Obj *pNew; /* Vector of new.* values */ int bIndirect; char *zPK; unsigned char *abPK; int i; sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect); pVar = Tcl_NewObj(); Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj( op==SQLITE_INSERT ? "INSERT" : op==SQLITE_UPDATE ? "UPDATE" : "DELETE", -1 )); Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zTab, -1)); Tcl_ListObjAppendElement(0, pVar, Tcl_NewBooleanObj(bIndirect)); zPK = ckalloc(nCol+1); memset(zPK, 0, nCol+1); sqlite3changeset_pk(pIter, &abPK, 0); for(i=0; i<nCol; i++){ zPK[i] = (abPK[i] ? 'X' : '.'); } Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zPK, -1)); ckfree(zPK); pOld = Tcl_NewObj(); if( op!=SQLITE_INSERT ){ int i; for(i=0; i<nCol; i++){ sqlite3_value *pVal; sqlite3changeset_old(pIter, i, &pVal); |
︙ | ︙ |