@@ -517,6 +517,23 @@ static int64_t json_extract_int(const char *json, size_t json_len, const char *k
517517 return strtoll (json + val -> start , NULL , 10 );
518518}
519519
520+ static int json_extract_array_size (const char * json , size_t json_len , const char * key ) {
521+ if (!json || json_len == 0 || !key ) return -1 ;
522+
523+ jsmn_parser parser ;
524+ jsmntok_t tokens [JSMN_MAX_TOKENS ];
525+ jsmn_init (& parser );
526+ int ntokens = jsmn_parse (& parser , json , json_len , tokens , JSMN_MAX_TOKENS );
527+ if (ntokens < 1 || tokens [0 ].type != JSMN_OBJECT ) return -1 ;
528+
529+ int i = jsmn_find_key (json , tokens , ntokens , key );
530+ if (i < 0 || i + 1 >= ntokens ) return -1 ;
531+
532+ jsmntok_t * val = & tokens [i + 1 ];
533+ if (val -> type != JSMN_ARRAY ) return -1 ;
534+
535+ return val -> size ;
536+ }
520537
521538int network_extract_query_param (const char * query , const char * key , char * output , size_t output_size ) {
522539 if (!query || !key || !output || output_size == 0 ) {
@@ -843,6 +860,23 @@ void cloudsync_network_set_apikey (sqlite3_context *context, int argc, sqlite3_v
843860 (result ) ? sqlite3_result_int (context , SQLITE_OK ) : sqlite3_result_error_code (context , SQLITE_NOMEM );
844861}
845862
863+ // MARK: - Sync result
864+
865+ typedef struct {
866+ int64_t server_version ; // lastOptimisticVersion
867+ int64_t local_version ; // new_db_version (max local)
868+ const char * status ; // computed status string
869+ int rows_received ; // rows from check
870+ } sync_result ;
871+
872+ static const char * network_compute_status (int64_t last_optimistic , int64_t last_confirmed ,
873+ int gaps_size , int64_t local_version ) {
874+ if (last_optimistic < 0 || last_confirmed < 0 ) return "error" ;
875+ if (gaps_size > 0 || last_optimistic < local_version ) return "retry" ;
876+ if (last_optimistic == last_confirmed ) return "synced" ;
877+ return "syncing" ;
878+ }
879+
846880// MARK: -
847881
848882void cloudsync_network_has_unsent_changes (sqlite3_context * context , int argc , sqlite3_value * * argv ) {
@@ -882,7 +916,7 @@ void cloudsync_network_has_unsent_changes (sqlite3_context *context, int argc, s
882916 sqlite3_result_int (context , (last_optimistic_version >= 0 && last_optimistic_version < last_local_change ));
883917}
884918
885- int cloudsync_network_send_changes_internal (sqlite3_context * context , int argc , sqlite3_value * * argv ) {
919+ int cloudsync_network_send_changes_internal (sqlite3_context * context , int argc , sqlite3_value * * argv , sync_result * out ) {
886920 DEBUG_FUNCTION ("cloudsync_network_send_changes" );
887921
888922 // retrieve global context
@@ -943,43 +977,63 @@ int cloudsync_network_send_changes_internal (sqlite3_context *context, int argc,
943977 res = network_receive_buffer (netdata , netdata -> apply_endpoint , netdata -> authentication , true, true, json_payload , CLOUDSYNC_HEADER_SQLITECLOUD );
944978 } else {
945979 // there is no data to send, just check the status to update the db_version value in settings and to reply the status
980+ new_db_version = db_version ;
946981 res = network_receive_buffer (netdata , netdata -> status_endpoint , netdata -> authentication , true, false, NULL , CLOUDSYNC_HEADER_SQLITECLOUD );
947982 }
948983
949984 int64_t last_optimistic_version = -1 ;
985+ int64_t last_confirmed_version = -1 ;
986+ int gaps_size = -1 ;
950987
951988 if (res .code == CLOUDSYNC_NETWORK_BUFFER && res .buffer ) {
952989 last_optimistic_version = json_extract_int (res .buffer , res .blen , "lastOptimisticVersion" , -1 );
990+ last_confirmed_version = json_extract_int (res .buffer , res .blen , "lastConfirmedVersion" , -1 );
991+ gaps_size = json_extract_array_size (res .buffer , res .blen , "gaps" );
992+ if (gaps_size < 0 ) gaps_size = 0 ;
953993 } else if (res .code != CLOUDSYNC_NETWORK_OK ) {
954994 network_result_to_sqlite_error (context , res , "cloudsync_network_send_changes unable to notify BLOB upload to remote host." );
955995 network_result_cleanup (& res );
956996 return SQLITE_ERROR ;
957997 }
958-
998+
959999 // update db_version in settings
9601000 char buf [256 ];
961- if (last_optimistic_version > 0 ) {
1001+ if (last_optimistic_version >= 0 ) {
9621002 if (last_optimistic_version != db_version ) {
9631003 snprintf (buf , sizeof (buf ), "%" PRId64 , last_optimistic_version );
9641004 dbutils_settings_set_key_value (data , CLOUDSYNC_KEY_SEND_DBVERSION , buf );
965- }
1005+ }
9661006 } else if (new_db_version != db_version ) {
9671007 snprintf (buf , sizeof (buf ), "%" PRId64 , new_db_version );
9681008 dbutils_settings_set_key_value (data , CLOUDSYNC_KEY_SEND_DBVERSION , buf );
9691009 }
970-
971- network_set_sqlite_result (context , & res );
1010+
1011+ // populate sync result
1012+ if (out ) {
1013+ out -> server_version = last_optimistic_version ;
1014+ out -> local_version = new_db_version ;
1015+ out -> status = network_compute_status (last_optimistic_version , last_confirmed_version , gaps_size , new_db_version );
1016+ }
1017+
9721018 network_result_cleanup (& res );
9731019 return SQLITE_OK ;
9741020}
9751021
9761022void cloudsync_network_send_changes (sqlite3_context * context , int argc , sqlite3_value * * argv ) {
9771023 DEBUG_FUNCTION ("cloudsync_network_send_changes" );
978-
979- cloudsync_network_send_changes_internal (context , argc , argv );
1024+
1025+ sync_result sr = {-1 , 0 , NULL , 0 };
1026+ int rc = cloudsync_network_send_changes_internal (context , argc , argv , & sr );
1027+ if (rc != SQLITE_OK ) return ;
1028+
1029+ char buf [256 ];
1030+ snprintf (buf , sizeof (buf ),
1031+ "{\"status\":\"%s\",\"localVersion\":%" PRId64 ",\"serverVersion\":%" PRId64 "}" ,
1032+ sr .status ? sr .status : "error" , sr .local_version , sr .server_version );
1033+ sqlite3_result_text (context , buf , -1 , SQLITE_TRANSIENT );
9801034}
9811035
982- int cloudsync_network_check_internal (sqlite3_context * context , int * pnrows ) {
1036+ int cloudsync_network_check_internal (sqlite3_context * context , int * pnrows , sync_result * out ) {
9831037 cloudsync_context * data = (cloudsync_context * )sqlite3_user_data (context );
9841038 network_data * netdata = (network_data * )cloudsync_auxdata (data );
9851039 if (!netdata ) {sqlite3_result_error (context , "Unable to retrieve CloudSync network context." , -1 ); return -1 ;}
@@ -1009,25 +1063,32 @@ int cloudsync_network_check_internal(sqlite3_context *context, int *pnrows) {
10091063 rc = network_set_sqlite_result (context , & result );
10101064 }
10111065
1066+ if (out && pnrows ) out -> rows_received = * pnrows ;
1067+
10121068 network_result_cleanup (& result );
10131069 return rc ;
10141070}
10151071
10161072void cloudsync_network_sync (sqlite3_context * context , int wait_ms , int max_retries ) {
1017- int rc = cloudsync_network_send_changes_internal (context , 0 , NULL );
1073+ sync_result sr = {-1 , 0 , NULL , 0 };
1074+ int rc = cloudsync_network_send_changes_internal (context , 0 , NULL , & sr );
10181075 if (rc != SQLITE_OK ) return ;
1019-
1076+
10201077 int ntries = 0 ;
10211078 int nrows = 0 ;
10221079 while (ntries < max_retries ) {
10231080 if (ntries > 0 ) sqlite3_sleep (wait_ms );
1024- rc = cloudsync_network_check_internal (context , & nrows );
1081+ rc = cloudsync_network_check_internal (context , & nrows , & sr );
10251082 if (rc == SQLITE_OK && nrows > 0 ) break ;
10261083 ntries ++ ;
10271084 }
1028-
1029- sqlite3_result_error_code (context , (nrows == -1 ) ? SQLITE_ERROR : SQLITE_OK );
1030- if (nrows >= 0 ) sqlite3_result_int (context , nrows );
1085+ if (rc != SQLITE_OK ) return ;
1086+
1087+ char buf [256 ];
1088+ snprintf (buf , sizeof (buf ),
1089+ "{\"status\":\"%s\",\"localVersion\":%" PRId64 ",\"serverVersion\":%" PRId64 ",\"rowsReceived\":%d}" ,
1090+ sr .status ? sr .status : "error" , sr .local_version , sr .server_version , nrows );
1091+ sqlite3_result_text (context , buf , -1 , SQLITE_TRANSIENT );
10311092}
10321093
10331094void cloudsync_network_sync0 (sqlite3_context * context , int argc , sqlite3_value * * argv ) {
@@ -1049,12 +1110,14 @@ void cloudsync_network_sync2 (sqlite3_context *context, int argc, sqlite3_value
10491110
10501111void cloudsync_network_check_changes (sqlite3_context * context , int argc , sqlite3_value * * argv ) {
10511112 DEBUG_FUNCTION ("cloudsync_network_check_changes" );
1052-
1113+
10531114 int nrows = 0 ;
1054- int rc = cloudsync_network_check_internal (context , & nrows );
1055-
1056- // returns number of applied rows
1057- if (rc == SQLITE_OK ) sqlite3_result_int (context , nrows );
1115+ int rc = cloudsync_network_check_internal (context , & nrows , NULL );
1116+ if (rc != SQLITE_OK ) return ;
1117+
1118+ char buf [128 ];
1119+ snprintf (buf , sizeof (buf ), "{\"rowsReceived\":%d}" , nrows );
1120+ sqlite3_result_text (context , buf , -1 , SQLITE_TRANSIENT );
10581121}
10591122
10601123void cloudsync_network_reset_sync_version (sqlite3_context * context , int argc , sqlite3_value * * argv ) {
0 commit comments