@@ -98,6 +98,19 @@ int sql_exec_changeset(
9898}
9999/************************************************************************/
100100
101+
102+ #ifdef SQLITE_DEBUG
103+ static int sqlite3_test_changeset (int , void * , char * * );
104+ static void assert_changeset_is_ok (int n , void * p ){
105+ int rc = 0 ;
106+ char * z = 0 ;
107+ rc = sqlite3_test_changeset (n , p , & z );
108+ assert ( z == 0 );
109+ }
110+ #else
111+ # define assert_changeset_is_ok (n ,p )
112+ #endif
113+
101114/*
102115** Tclcmd: sql_exec_changeset DB SQL
103116*/
@@ -127,6 +140,7 @@ static int SQLITE_TCLAPI test_sql_exec_changeset(
127140 return TCL_ERROR ;
128141 }
129142
143+ assert_changeset_is_ok (nChangeset , pChangeset );
130144 Tcl_SetObjResult (interp , Tcl_NewByteArrayObj (pChangeset , nChangeset ));
131145 sqlite3_free (pChangeset );
132146 return TCL_OK ;
@@ -295,6 +309,7 @@ static int SQLITE_TCLAPI test_session_cmd(
295309 }
296310 }
297311 if ( rc == SQLITE_OK ){
312+ assert_changeset_is_ok (o .n , o .p );
298313 Tcl_SetObjResult (interp , Tcl_NewByteArrayObj (o .p , o .n ));
299314 }
300315 sqlite3_free (o .p );
@@ -953,6 +968,7 @@ static int SQLITE_TCLAPI test_sqlite3changeset_invert(
953968 if ( rc != SQLITE_OK ){
954969 rc = test_session_error (interp , rc , 0 );
955970 }else {
971+ assert_changeset_is_ok (sOut .n , sOut .p );
956972 Tcl_SetObjResult (interp ,Tcl_NewByteArrayObj ((unsigned char * )sOut .p ,sOut .n ));
957973 }
958974 sqlite3_free (sOut .p );
@@ -1001,6 +1017,7 @@ static int SQLITE_TCLAPI test_sqlite3changeset_concat(
10011017 if ( rc != SQLITE_OK ){
10021018 rc = test_session_error (interp , rc , 0 );
10031019 }else {
1020+ assert_changeset_is_ok (sOut .n , sOut .p );
10041021 Tcl_SetObjResult (interp ,Tcl_NewByteArrayObj ((unsigned char * )sOut .p ,sOut .n ));
10051022 }
10061023 sqlite3_free (sOut .p );
@@ -1236,6 +1253,7 @@ static int SQLITE_TCLAPI test_rebaser_cmd(
12361253 }
12371254
12381255 if ( rc == SQLITE_OK ){
1256+ assert_changeset_is_ok (sOut .n , sOut .p );
12391257 Tcl_SetObjResult (interp , Tcl_NewByteArrayObj (sOut .p , sOut .n ));
12401258 }
12411259 sqlite3_free (sOut .p );
@@ -1282,6 +1300,107 @@ static int SQLITE_TCLAPI test_sqlite3rebaser_create(
12821300 return TCL_OK ;
12831301}
12841302
1303+ /*
1304+ ** Run some sanity checks on the changeset in nChangeset byte buffer
1305+ ** pChangeset. If any fail, return a non-zero value and, optionally,
1306+ ** set output variable (*pzErr) to point to a buffer containing an
1307+ ** English language error message describing the problem. In this
1308+ ** case it is the responsibility of the caller to free the buffer
1309+ ** using sqlite3_free().
1310+ **
1311+ ** Or, if the changeset appears to be well-formed, this function
1312+ ** returns SQLITE_OK and sets (*pzErr) to NULL.
1313+ */
1314+ static int sqlite3_test_changeset (
1315+ int nChangeset ,
1316+ void * pChangeset ,
1317+ char * * pzErr
1318+ ){
1319+ sqlite3_changeset_iter * pIter = 0 ;
1320+ char * zErr = 0 ;
1321+ int rc = SQLITE_OK ;
1322+ int bPatch = (nChangeset > 0 && ((char * )pChangeset )[0 ]== 'P' );
1323+
1324+ rc = sqlite3changeset_start (& pIter , nChangeset , pChangeset );
1325+ if ( rc == SQLITE_OK ){
1326+ int rc2 ;
1327+ while ( rc == SQLITE_OK && SQLITE_ROW == sqlite3changeset_next (pIter ) ){
1328+ unsigned char * aPk = 0 ;
1329+ int nCol = 0 ;
1330+ int op = 0 ;
1331+ const char * zTab = 0 ;
1332+
1333+ sqlite3changeset_pk (pIter , & aPk , & nCol );
1334+ sqlite3changeset_op (pIter , & zTab , & nCol , & op , 0 );
1335+
1336+ if ( op == SQLITE_UPDATE ){
1337+ int iCol ;
1338+ for (iCol = 0 ; iCol < nCol ; iCol ++ ){
1339+ sqlite3_value * pNew = 0 ;
1340+ sqlite3_value * pOld = 0 ;
1341+ sqlite3changeset_new (pIter , iCol , & pNew );
1342+ sqlite3changeset_old (pIter , iCol , & pOld );
1343+
1344+ if ( aPk [iCol ] ){
1345+ if ( pOld == 0 ) rc = SQLITE_ERROR ;
1346+ }else if ( bPatch ){
1347+ if ( pOld ) rc = SQLITE_ERROR ;
1348+ }else {
1349+ if ( (pOld == 0 )!= (pNew == 0 ) ) rc = SQLITE_ERROR ;
1350+ }
1351+
1352+ if ( rc != SQLITE_OK ){
1353+ zErr = sqlite3_mprintf (
1354+ "unexpected SQLITE_UPDATE (bPatch=%d pk=%d pOld=%d pNew=%d)" ,
1355+ bPatch , (int )aPk [iCol ], pOld != 0 , pNew != 0
1356+ );
1357+ break ;
1358+ }
1359+ }
1360+ }
1361+ }
1362+ rc2 = sqlite3changeset_finalize (pIter );
1363+ if ( rc == SQLITE_OK ){
1364+ rc = rc2 ;
1365+ }
1366+ }
1367+
1368+ * pzErr = zErr ;
1369+ return rc ;
1370+ }
1371+
1372+ /*
1373+ ** test_changeset CHANGESET
1374+ */
1375+ static int SQLITE_TCLAPI test_changeset (
1376+ void * clientData ,
1377+ Tcl_Interp * interp ,
1378+ int objc ,
1379+ Tcl_Obj * CONST objv []
1380+ ){
1381+ void * pChangeset = 0 ; /* Buffer containing changeset */
1382+ int nChangeset = 0 ; /* Size of buffer aChangeset in bytes */
1383+ int rc = SQLITE_OK ;
1384+ char * z = 0 ;
1385+
1386+ if ( objc != 2 ){
1387+ Tcl_WrongNumArgs (interp , 1 , objv , "CHANGESET" );
1388+ return TCL_ERROR ;
1389+ }
1390+ pChangeset = (void * )Tcl_GetByteArrayFromObj (objv [1 ], & nChangeset );
1391+
1392+ Tcl_ResetResult (interp );
1393+ rc = sqlite3_test_changeset (nChangeset , pChangeset , & z );
1394+ if ( rc != SQLITE_OK ){
1395+ char * zErr = sqlite3_mprintf ("(%d) - \"%s\"" , rc , z );
1396+ Tcl_SetObjResult (interp , Tcl_NewStringObj (zErr , -1 ));
1397+ sqlite3_free (zErr );
1398+ }
1399+ sqlite3_free (z );
1400+
1401+ return rc ? TCL_ERROR : TCL_OK ;
1402+ }
1403+
12851404/*
12861405** tclcmd: sqlite3rebaser_configure OP VALUE
12871406*/
@@ -1337,6 +1456,7 @@ int TestSession_Init(Tcl_Interp *interp){
13371456 { "sql_exec_changeset" , test_sql_exec_changeset },
13381457 { "sqlite3rebaser_create" , test_sqlite3rebaser_create },
13391458 { "sqlite3session_config" , test_sqlite3session_config },
1459+ { "test_changeset" , test_changeset },
13401460 };
13411461 int i ;
13421462
0 commit comments