Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Add the sqlite3changegroup_xxx() APIs to the sessions module. For combining multiple changesets or patchsets. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | sessions |
Files: | files | file ages | folders |
SHA1: |
0c1a901cd60e557fc676b97625243163 |
User & Date: | dan 2015-06-11 17:26:10.939 |
Context
2015-06-11
| ||
18:01 | Merge recent trunk enhancements and fixes. (check-in: c39cb0e257 user: drh tags: sessions) | |
17:26 | Add the sqlite3changegroup_xxx() APIs to the sessions module. For combining multiple changesets or patchsets. (check-in: 0c1a901cd6 user: dan tags: sessions) | |
2015-06-02
| ||
09:20 | Add the "finish_test" command to the end of new test script sessionE.test. (check-in: fb39140707 user: dan tags: sessions) | |
Changes
Changes to ext/session/sqlite3session.c.
︙ | ︙ | |||
4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 | int rc = sqlite3changeset_start_strm(&pIter, xInput, pIn); if( rc==SQLITE_OK ){ rc = sessionChangesetApply(db, pIter, xFilter, xConflict, pCtx); } return rc; } /* ** This function is called to merge two changes to the same row together as ** part of an sqlite3changeset_concat() operation. A new change object is ** allocated and a pointer to it stored in *ppNew. */ static int sessionChangeMerge( SessionTable *pTab, /* Table structure */ | > > > > > > > > > | 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 | int rc = sqlite3changeset_start_strm(&pIter, xInput, pIn); if( rc==SQLITE_OK ){ rc = sessionChangesetApply(db, pIter, xFilter, xConflict, pCtx); } return rc; } /* ** sqlite3_changegroup handle. */ struct sqlite3_changegroup { int rc; /* Error code */ int bPatch; /* True to accumulate patchsets */ SessionTable *pList; /* List of tables in current patch */ }; /* ** This function is called to merge two changes to the same row together as ** part of an sqlite3changeset_concat() operation. A new change object is ** allocated and a pointer to it stored in *ppNew. */ static int sessionChangeMerge( SessionTable *pTab, /* Table structure */ |
︙ | ︙ | |||
4166 4167 4168 4169 4170 4171 4172 | } *ppNew = pNew; return SQLITE_OK; } /* | | | | > > > > > > > > | | | | 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 | } *ppNew = pNew; return SQLITE_OK; } /* ** Add all changes in the changeset traversed by the iterator passed as ** the first argument to the changegroup hash tables. */ static int sessionChangesetToHash( sqlite3_changeset_iter *pIter, /* Iterator to read from */ sqlite3_changegroup *pGrp /* Changegroup object to add changeset to */ ){ u8 *aRec; int nRec; int rc = SQLITE_OK; SessionTable *pTab = 0; while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec) ){ const char *zNew; int nCol; int op; int iHash; int bIndirect; SessionChange *pChange; SessionChange *pExist = 0; SessionChange **pp; if( pGrp->pList==0 ){ pGrp->bPatch = pIter->bPatchset; }else if( pIter->bPatchset!=pGrp->bPatch ){ rc = SQLITE_ERROR; break; } sqlite3changeset_op(pIter, &zNew, &nCol, &op, &bIndirect); if( !pTab || sqlite3_stricmp(zNew, pTab->zName) ){ /* Search the list for a matching table */ int nNew = (int)strlen(zNew); u8 *abPK; sqlite3changeset_pk(pIter, &abPK, 0); for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){ if( 0==sqlite3_strnicmp(pTab->zName, zNew, nNew+1) ) break; } if( !pTab ){ pTab = sqlite3_malloc(sizeof(SessionTable) + nCol + nNew+1); if( !pTab ){ rc = SQLITE_NOMEM; break; } memset(pTab, 0, sizeof(SessionTable)); pTab->pNext = pGrp->pList; pTab->nCol = nCol; pTab->abPK = (u8*)&pTab[1]; memcpy(pTab->abPK, abPK, nCol); pTab->zName = (char*)&pTab->abPK[nCol]; memcpy(pTab->zName, zNew, nNew+1); pGrp->pList = pTab; }else if( pTab->nCol!=nCol || memcmp(pTab->abPK, abPK, nCol) ){ rc = SQLITE_SCHEMA; break; } } if( sessionGrowHash(pIter->bPatchset, pTab) ){ |
︙ | ︙ | |||
4258 4259 4260 4261 4262 4263 4264 | pTab->nEntry++; } } if( rc==SQLITE_OK ) rc = pIter->rc; return rc; } | | | | < | | < | > > | > > > > > > > | | < < | < < | < < < < < < < < < < < < | | | | 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 | pTab->nEntry++; } } if( rc==SQLITE_OK ) rc = pIter->rc; return rc; } /* ** Serialize a changeset (or patchset) based on all changesets (or patchsets) ** added to the changegroup object passed as the first argument. ** ** If xOutput is not NULL, then the changeset/patchset is returned to the ** user via one or more calls to xOutput, as with the other streaming ** interfaces. ** ** Or, if xOutput is NULL, then (*ppOut) is populated with a pointer to a ** buffer containing the output changeset before this function returns. In ** this case (*pnOut) is set to the size of the output buffer in bytes. It ** is the responsibility of the caller to free the output buffer using ** sqlite3_free() when it is no longer required. ** ** If successful, SQLITE_OK is returned. Or, if an error occurs, an SQLite ** error code. If an error occurs and xOutput is NULL, (*ppOut) and (*pnOut) ** are both set to 0 before returning. */ static int sessionChangegroupOutput( sqlite3_changegroup *pGrp, int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut, int *pnOut, void **ppOut ){ int rc = SQLITE_OK; SessionBuffer buf = {0, 0, 0}; SessionTable *pTab; assert( xOutput==0 || (ppOut==0 && pnOut==0) ); /* Create the serialized output changeset based on the contents of the ** hash tables attached to the SessionTable objects in list p->pList. */ for(pTab=pGrp->pList; rc==SQLITE_OK && pTab; pTab=pTab->pNext){ int i; if( pTab->nEntry==0 ) continue; sessionAppendTableHdr(&buf, pGrp->bPatch, pTab, &rc); for(i=0; i<pTab->nChange; i++){ SessionChange *p; for(p=pTab->apChange[i]; p; p=p->pNext){ sessionAppendByte(&buf, p->op, &rc); sessionAppendByte(&buf, p->bIndirect, &rc); sessionAppendBlob(&buf, p->aRecord, p->nRecord, &rc); } |
︙ | ︙ | |||
4332 4333 4334 4335 4336 4337 4338 | *ppOut = buf.aBuf; *pnOut = buf.nBuf; buf.aBuf = 0; } } sqlite3_free(buf.aBuf); | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < | | | > > | | | | < | | | > > | | | | 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 | *ppOut = buf.aBuf; *pnOut = buf.nBuf; buf.aBuf = 0; } } sqlite3_free(buf.aBuf); return rc; } /* ** Allocate a new, empty, sqlite3_changegroup. */ int sqlite3changegroup_new(sqlite3_changegroup **pp){ int rc = SQLITE_OK; /* Return code */ sqlite3_changegroup *p; /* New object */ p = (sqlite3_changegroup*)sqlite3_malloc(sizeof(sqlite3_changegroup)); if( p==0 ){ rc = SQLITE_NOMEM; }else{ memset(p, 0, sizeof(sqlite3_changegroup)); } *pp = p; return rc; } /* ** Add the changeset currently stored in buffer pData, size nData bytes, ** to changeset-group p. */ int sqlite3changegroup_add(sqlite3_changegroup *pGrp, int nData, void *pData){ sqlite3_changeset_iter *pIter; /* Iterator opened on pData/nData */ int rc; /* Return code */ rc = sqlite3changeset_start(&pIter, nData, pData); if( rc==SQLITE_OK ){ rc = sessionChangesetToHash(pIter, pGrp); } sqlite3changeset_finalize(pIter); return rc; } /* ** Obtain a buffer containing a changeset representing the concatenation ** of all changesets added to the group so far. */ int sqlite3changegroup_output( sqlite3_changegroup *pGrp, int *pnData, void **ppData ){ return sessionChangegroupOutput(pGrp, 0, 0, pnData, ppData); } /* ** Streaming versions of changegroup_add(). */ int sqlite3changegroup_add_strm( sqlite3_changegroup *pGrp, int (*xInput)(void *pIn, void *pData, int *pnData), void *pIn ){ sqlite3_changeset_iter *pIter; /* Iterator opened on pData/nData */ int rc; /* Return code */ rc = sqlite3changeset_start_strm(&pIter, xInput, pIn); if( rc==SQLITE_OK ){ rc = sessionChangesetToHash(pIter, pGrp); } sqlite3changeset_finalize(pIter); return rc; } /* ** Streaming versions of changegroup_output(). */ int sqlite3changegroup_output_strm( sqlite3_changegroup *pGrp, int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut ){ return sessionChangegroupOutput(pGrp, xOutput, pOut, 0, 0); } /* ** Delete a changegroup object. */ void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){ if( pGrp ){ sessionDeleteTable(pGrp->pList); sqlite3_free(pGrp); } } /* ** Combine two changesets together. */ int sqlite3changeset_concat( int nLeft, /* Number of bytes in lhs input */ void *pLeft, /* Lhs input changeset */ int nRight /* Number of bytes in rhs input */, void *pRight, /* Rhs input changeset */ int *pnOut, /* OUT: Number of bytes in output changeset */ void **ppOut /* OUT: changeset (left <concat> right) */ ){ sqlite3_changegroup *pGrp; int rc; rc = sqlite3changegroup_new(&pGrp); if( rc==SQLITE_OK ){ rc = sqlite3changegroup_add(pGrp, nLeft, pLeft); } if( rc==SQLITE_OK ){ rc = sqlite3changegroup_add(pGrp, nRight, pRight); } if( rc==SQLITE_OK ){ rc = sqlite3changegroup_output(pGrp, pnOut, ppOut); } sqlite3changegroup_delete(pGrp); return rc; } /* ** Streaming version of sqlite3changeset_concat(). */ int sqlite3changeset_concat_strm( int (*xInputA)(void *pIn, void *pData, int *pnData), void *pInA, int (*xInputB)(void *pIn, void *pData, int *pnData), void *pInB, int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut ){ sqlite3_changegroup *pGrp; int rc; rc = sqlite3changegroup_new(&pGrp); if( rc==SQLITE_OK ){ rc = sqlite3changegroup_add_strm(pGrp, xInputA, pInA); } if( rc==SQLITE_OK ){ rc = sqlite3changegroup_add_strm(pGrp, xInputB, pInB); } if( rc==SQLITE_OK ){ rc = sqlite3changegroup_output_strm(pGrp, xOutput, pOut); } sqlite3changegroup_delete(pGrp); return rc; } #endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */ |
Changes to ext/session/sqlite3session.h.
︙ | ︙ | |||
679 680 681 682 683 684 685 | /* ** CAPI3REF: Concatenate Two Changeset Objects ** ** This function is used to concatenate two changesets, A and B, into a ** single changeset. The result is a changeset equivalent to applying ** changeset A followed by changeset B. ** | < < < < < | < > | < < < < < < < | | < < < | < | < < | < < < | < < < < < < < | < < < | < < < | < | < < < < < < < < < < | < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 | /* ** CAPI3REF: Concatenate Two Changeset Objects ** ** This function is used to concatenate two changesets, A and B, into a ** single changeset. The result is a changeset equivalent to applying ** changeset A followed by changeset B. ** ** This function combines the two input changesets using an ** sqlite3_changegroup object. Calling it produces similar results as the ** following code fragment: ** ** sqlite3_changegroup *pGrp; ** rc = sqlite3_changegroup_new(&pGrp); ** if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nA, pA); ** if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nB, pB); ** if( rc==SQLITE_OK ){ ** rc = sqlite3changegroup_output(pGrp, pnOut, ppOut); ** }else{ ** *ppOut = 0; ** *pnOut = 0; ** } ** ** Refer to the sqlite3_changegroup documentation below for details. */ int sqlite3changeset_concat( int nA, /* Number of bytes in buffer pA */ void *pA, /* Pointer to buffer containing changeset A */ int nB, /* Number of bytes in buffer pB */ void *pB, /* Pointer to buffer containing changeset B */ int *pnOut, /* OUT: Number of bytes in output changeset */ void **ppOut /* OUT: Buffer containing output changeset */ ); /* ** Changegroup handle. */ typedef struct sqlite3_changegroup sqlite3_changegroup; /* ** CAPI3REF: Combine two or more changesets into a single changeset. ** ** An sqlite3_changegroup object is used to combine two or more changesets ** (or patchsets) into a single changeset (or patchset). A single changegroup ** object may combine changesets or patchsets, but not both. The output is ** always in the same format as the input. ** ** If successful, this function returns SQLITE_OK and populates (*pp) with ** a pointer to a new sqlite3_changegroup object before returning. The caller ** should eventually free the returned object using a call to ** sqlite3changegroup_delete(). If an error occurs, an SQLite error code ** (i.e. SQLITE_NOMEM) is returned and *pp is set to NULL. ** ** The usual usage pattern for an sqlite3_changegroup object is as follows: ** ** <ul> ** <li> It is created using a call to sqlite3changegroup_new(). ** ** <li> Zero or more changesets (or patchsets) are added to the object ** by calling sqlite3changegroup_add(). ** ** <li> The result of combining all input changesets together is obtained ** by the application via a call to sqlite3changegroup_output(). ** ** <li> The object is deleted using a call to sqlite3changegroup_delete(). ** </ul> ** ** Any number of calls to add() and output() may be made between the calls to ** new() and delete(), and in any order. ** ** As well as the regular sqlite3changegroup_add() and ** sqlite3changegroup_output() functions, also available are the streaming ** versions sqlite3changegroup_add_strm() and sqlite3changegroup_output_strm(). */ int sqlite3changegroup_new(sqlite3_changegroup **pp); /* ** Add all changes within the changeset (or patchset) in buffer pData (size ** nData bytes) to the changegroup. ** ** If the buffer contains a patchset, then all prior calls to this function ** on the same changegroup object must also have specified patchsets. Or, if ** the buffer contains a changeset, so must have the earlier calls to this ** function. Otherwise, SQLITE_ERROR is returned and no changes are added ** to the changegroup. ** ** Rows within the changeset and changegroup are identified by the values in ** their PRIMARY KEY columns. A change in the changeset is considered to ** apply to the same row as a change already present in the changegroup if ** the two rows have the same primary key. ** ** Changes to rows that that do not already appear in the changegroup are ** simply copied into it. Or, if both the new changeset and the changegroup ** contain changes that apply to a single row, the final contents of the ** changegroup depends on the type of each change, as follows: ** ** <table border=1 style="margin-left:8ex;margin-right:8ex"> ** <tr><th style="white-space:pre">Existing Change </th> ** <th style="white-space:pre">New Change </th> ** <th>Output Change ** <tr><td>INSERT <td>INSERT <td> ** The new change is ignored. This case does not occur if the new ** changeset was recorded immediately after the changesets already ** added to the changegroup. ** <tr><td>INSERT <td>UPDATE <td> ** The INSERT change remains in the changegroup. The values in the ** INSERT change are modified as if the row was inserted by the ** existing change and then updated according to the new change. ** <tr><td>INSERT <td>DELETE <td> ** The existing INSERT is removed from the changegroup. The DELETE is ** not added. ** <tr><td>UPDATE <td>INSERT <td> ** The new change is ignored. This case does not occur if the new ** changeset was recorded immediately after the changesets already ** added to the changegroup. ** <tr><td>UPDATE <td>UPDATE <td> ** The existing UPDATE remains within the changegroup. It is amended ** so that the accompanying values are as if the row was updated once ** by the existing change and then again by the new change. ** <tr><td>UPDATE <td>DELETE <td> ** The existing UPDATE is replaced by the new DELETE within the ** changegroup. ** <tr><td>DELETE <td>INSERT <td> ** If one or more of the column values in the row inserted by the ** new change differ from those in the row deleted by the existing ** change, the existing DELETE is replaced by an UPDATE within the ** changegroup. Otherwise, if the inserted row is exactly the same ** as the deleted row, the existing DELETE is simply discarded. ** <tr><td>DELETE <td>UPDATE <td> ** The new change is ignored. This case does not occur if the new ** changeset was recorded immediately after the changesets already ** added to the changegroup. ** <tr><td>DELETE <td>DELETE <td> ** The new change is ignored. This case does not occur if the new ** changeset was recorded immediately after the changesets already ** added to the changegroup. ** </table> ** ** If the new changeset contains changes to a table that is already present ** in the changegroup, then the number of columns and the position of the ** primary key columns for the table must be consistent. If this is not the ** case, this function fails with SQLITE_SCHEMA. If the input changeset ** appears to be corrupt and the corruption is detected, SQLITE_CORRUPT is ** returned. Or, if an out-of-memory condition occurs during processing, this ** function returns SQLITE_NOMEM. In all cases, if an error occurs the ** final contents of the changegroup is undefined. ** ** If no error occurs, SQLITE_OK is returned. */ int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); /* ** Obtain a buffer containing a changeset (or patchset) representing the ** current contents of the changegroup. If the inputs to the changegroup ** were themselves changesets, the output is a changeset. Or, if the ** inputs were patchsets, the output is also a patchset. ** ** If an error occurs, an SQLite error code is returned and the output ** variables (*pnData) and (*ppData) are set to 0. Otherwise, SQLITE_OK ** is returned and the output variables are set to the size of and a ** pointer to the output buffer, respectively. In this case it is the ** responsibility of the caller to eventually free the buffer using a ** call to sqlite3_free(). */ int sqlite3changegroup_output( sqlite3_changegroup*, int *pnData, /* OUT: Size of output buffer in bytes */ void **ppData /* OUT: Pointer to output buffer */ ); /* ** Delete a changegroup object. */ void sqlite3changegroup_delete(sqlite3_changegroup*); /* ** CAPI3REF: Apply A Changeset To A Database ** ** Apply a changeset to a database. This function attempts to update the ** "main" database attached to handle db with the changes found in the ** changeset passed via the second and third arguments. ** |
︙ | ︙ | |||
1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 | int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut ); int sqlite3session_patchset_strm( sqlite3_session *pSession, int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut ); /* ** Make sure we can call this stuff from C++. */ #ifdef __cplusplus } #endif #endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */ | > > > > > > > > | 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 | int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut ); int sqlite3session_patchset_strm( sqlite3_session *pSession, int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut ); int sqlite3changegroup_add_strm(sqlite3_changegroup*, int (*xInput)(void *pIn, void *pData, int *pnData), void *pIn ); int sqlite3changegroup_output_strm(sqlite3_changegroup*, int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut ); /* ** Make sure we can call this stuff from C++. */ #ifdef __cplusplus } #endif #endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */ |