Fossil

Check-in [95f5520a09]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Construct event records for tickets correctly even when the ticket change artifacts arrive out of order.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 95f5520a09efb7698179f5123fbca8e3f33fbb39
User & Date: drh 2009-09-14 14:08:35.000
Context
2009-09-14
14:10
Make sure the manifest_crosslink_begin/end pair can be called multiple times. ... (check-in: 2a069086e9 user: drh tags: trunk)
14:08
Construct event records for tickets correctly even when the ticket change artifacts arrive out of order. ... (check-in: 95f5520a09 user: drh tags: trunk)
2009-09-13
16:19
On windows, if the first character of a pathname is '\' then assume that is a full pathname, not a relative pathname. Ticket [cdd360438de]. ... (check-in: 42bf80978d user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/manifest.c.
879
880
881
882
883
884
885



































































































886
887
888
889
890
891
892
    }else{
      add_one_mlink(cid, 0, pChild->aFile[j].zUuid, pChild->aFile[j].zName,0);
    }
    j++;
  }
  manifest_clear(&other);
}




































































































/*
** Scan artifact rid/pContent to see if it is a control artifact of
** any key:
**
**      *  Manifest
**      *  Control







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
    }else{
      add_one_mlink(cid, 0, pChild->aFile[j].zUuid, pChild->aFile[j].zName,0);
    }
    j++;
  }
  manifest_clear(&other);
}

/*
** True if manifest_crosslink_begin() has been called but
** manifest_crosslink_end() is still pending.
*/
static int manifest_crosslink_busy = 0;

/*
** Setup to do multiple manifest_crosslink() calls.
** This is only required if processing ticket changes.
*/
void manifest_crosslink_begin(void){
  assert( manifest_crosslink_busy==0 );
  manifest_crosslink_busy = 1;
  db_begin_transaction();
  db_multi_exec("CREATE TEMP TABLE pending_tkt(uuid TEXT UNIQUE)");
}

/*
** Finish up a sequence of manifest_crosslink calls.
*/
void manifest_crosslink_end(void){
  Stmt q;
  assert( manifest_crosslink_busy==1 );
  db_prepare(&q, "SELECT uuid FROM pending_tkt");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zUuid = db_column_text(&q, 0);
    ticket_rebuild_entry(zUuid);
  }
  db_finalize(&q);
  db_end_transaction(0);
  manifest_crosslink_busy = 0;
}

/*
** Make an entry in the event table for a ticket change artifact.
*/
void manifest_ticket_event(
  int rid,                    /* Artifact ID of the change ticket artifact */
  const Manifest *pManifest,  /* Parsed content of the artifact */
  int isNew                   /* True if this is the first event */
){
  int i;
  char *zTitle;
  Blob comment;
  char *zNewStatus = 0;
  static char *zTitleExpr = 0;
  static char *zStatusColumn = 0;
  static int once = 1;

  blob_zero(&comment);
  if( once ){
    once = 0;
    zTitleExpr = db_get("ticket-title-expr", "title");
    zStatusColumn = db_get("ticket-status-column", "status");
  }
  zTitle = db_text("unknown", 
    "SELECT %s FROM ticket WHERE tkt_uuid='%s'",
    zTitleExpr, pManifest->zTicketUuid
  );
  if( !isNew ){
    for(i=0; i<pManifest->nField; i++){
      if( strcmp(pManifest->aField[i].zName, zStatusColumn)==0 ){
        zNewStatus = pManifest->aField[i].zValue;
      }
    }
    if( zNewStatus ){
      blob_appendf(&comment, "%h ticket [%.10s]: <i>%h</i>",
         zNewStatus, pManifest->zTicketUuid, zTitle
      );
      if( pManifest->nField>1 ){
        blob_appendf(&comment, " plus %d other change%s",
          pManifest->nField-1, pManifest->nField==2 ? "" : "s");
      }
    }else{
      zNewStatus = db_text("unknown", 
         "SELECT %s FROM ticket WHERE tkt_uuid='%s'",
         zStatusColumn, pManifest->zTicketUuid
      );
      blob_appendf(&comment, "Ticket [%.10s] <i>%h</i> status still %h with "
           "%d other change%s",
           pManifest->zTicketUuid, zTitle, zNewStatus, pManifest->nField,
           pManifest->nField==1 ? "" : "s"
      );
      free(zNewStatus);
    }
  }else{
    blob_appendf(&comment, "New ticket [%.10s] <i>%h</i>.",
      pManifest->zTicketUuid, zTitle
    );
  }
  free(zTitle);
  db_multi_exec(
    "REPLACE INTO event(type,mtime,objid,user,comment)"
    "VALUES('t',%.17g,%d,%Q,%Q)",
    pManifest->rDate, rid, pManifest->zUser, blob_str(&comment)
  );
  blob_reset(&comment);
}

/*
** Scan artifact rid/pContent to see if it is a control artifact of
** any key:
**
**      *  Manifest
**      *  Control
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080

1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
      TAG_BGCOLOR, rid,
      TAG_USER, rid,
      TAG_COMMENT, rid
    );
    free(zComment);
  }
  if( m.type==CFTYPE_TICKET ){
    int i;
    char *zTitle;
    char *zTag;
    Blob comment;
    char *zNewStatus = 0;
    static char *zTitleExpr = 0;
    static char *zStatusColumn = 0;
    static int once = 1;
    int isNew;

    isNew = ticket_insert(&m, 1, 1);
    zTag = mprintf("tkt-%s", m.zTicketUuid);
    tag_insert(zTag, 1, 0, rid, m.rDate, rid);
    free(zTag);
    blob_zero(&comment);
    if( once ){
      once = 0;
      zTitleExpr = db_get("ticket-title-expr", "title");
      zStatusColumn = db_get("ticket-status-column", "status");
    }
    zTitle = db_text("unknown", 
      "SELECT %s FROM ticket WHERE tkt_uuid='%s'",
      zTitleExpr, m.zTicketUuid
    );
    if( !isNew ){
      for(i=0; i<m.nField; i++){
        if( strcmp(m.aField[i].zName, zStatusColumn)==0 ){
          zNewStatus = m.aField[i].zValue;
        }
      }
      if( zNewStatus ){
        blob_appendf(&comment, "%h ticket [%.10s]: <i>%h</i>",
           zNewStatus, m.zTicketUuid, zTitle
        );
        if( m.nField>1 ){
          blob_appendf(&comment, " plus %d other change%s",
            m.nField-1, m.nField==2 ? "" : "s");
        }
      }else{
        zNewStatus = db_text("unknown", 
           "SELECT %s FROM ticket WHERE tkt_uuid='%s'",
           zStatusColumn, m.zTicketUuid
        );
        blob_appendf(&comment, "Ticket [%.10s] <i>%h</i> status still %h with "
             "%d other change%s",
             m.zTicketUuid, zTitle, zNewStatus, m.nField,
             m.nField==1 ? "" : "s"
        );
        free(zNewStatus);
      }
    }else{
      blob_appendf(&comment, "New ticket [%.10s] <i>%h</i>.",

        m.zTicketUuid, zTitle
      );
    }
    free(zTitle);
    db_multi_exec(
      "REPLACE INTO event(type,mtime,objid,user,comment)"
      "VALUES('t',%.17g,%d,%Q,%Q)",
      m.rDate, rid, m.zUser, blob_str(&comment)
    );
    blob_reset(&comment);
  }
  db_end_transaction(0);
  manifest_clear(&m);
  return 1;
}







<
<

<
<
<
<
<
<

|



<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
>
|
<
|
<
<
<
<
<
<
<
<




1121
1122
1123
1124
1125
1126
1127


1128






1129
1130
1131
1132
1133






































1134
1135

1136








1137
1138
1139
1140
      TAG_BGCOLOR, rid,
      TAG_USER, rid,
      TAG_COMMENT, rid
    );
    free(zComment);
  }
  if( m.type==CFTYPE_TICKET ){


    char *zTag;







    assert( manifest_crosslink_busy==1 );
    zTag = mprintf("tkt-%s", m.zTicketUuid);
    tag_insert(zTag, 1, 0, rid, m.rDate, rid);
    free(zTag);






































    db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
                  m.zTicketUuid);

  }








  db_end_transaction(0);
  manifest_clear(&m);
  return 1;
}
Changes to src/rebuild.c.
239
240
241
242
243
244
245

246
247
248
249
250
251
252
  );
  totalSize = db_int(0, "SELECT count(*) FROM blob");
  db_prepare(&s,
     "SELECT rid, size FROM blob"
     " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
     "   AND NOT EXISTS(SELECT 1 FROM delta WHERE rid=blob.rid)"
  );

  while( db_step(&s)==SQLITE_ROW ){
    int rid = db_column_int(&s, 0);
    int size = db_column_int(&s, 1);
    if( size>=0 ){
      Blob content;
      content_get(rid, &content);
      rebuild_step(rid, size, &content);







>







239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
  );
  totalSize = db_int(0, "SELECT count(*) FROM blob");
  db_prepare(&s,
     "SELECT rid, size FROM blob"
     " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
     "   AND NOT EXISTS(SELECT 1 FROM delta WHERE rid=blob.rid)"
  );
  manifest_crosslink_begin();
  while( db_step(&s)==SQLITE_ROW ){
    int rid = db_column_int(&s, 0);
    int size = db_column_int(&s, 1);
    if( size>=0 ){
      Blob content;
      content_get(rid, &content);
      rebuild_step(rid, size, &content);
268
269
270
271
272
273
274

275
276
277
278
279
280
281
      }
    }else{
      db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
      rebuild_step_done(rid);
    }
  }
  db_finalize(&s);

  rebuild_tag_trunk();
  if( ttyOutput ){
    printf("\n");
  }
  return errCnt;
}








>







269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
      }
    }else{
      db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
      rebuild_step_done(rid);
    }
  }
  db_finalize(&s);
  manifest_crosslink_end();
  rebuild_tag_trunk();
  if( ttyOutput ){
    printf("\n");
  }
  return errCnt;
}

Changes to src/tag.c.
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
  }
  return id;
}

/*
** Insert a tag into the database.
*/
void tag_insert(
  const char *zTag,        /* Name of the tag (w/o the "+" or "-" prefix */
  int tagtype,             /* 0:cancel  1:singleton  2:propagated */
  const char *zValue,      /* Value if the tag is really a property */
  int srcId,               /* Artifact that contains this tag */
  double mtime,            /* Timestamp.  Use default if <=0.0 */
  int rid                  /* Artifact to which the tag is to attached */
){







|







140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
  }
  return id;
}

/*
** Insert a tag into the database.
*/
int tag_insert(
  const char *zTag,        /* Name of the tag (w/o the "+" or "-" prefix */
  int tagtype,             /* 0:cancel  1:singleton  2:propagated */
  const char *zValue,      /* Value if the tag is really a property */
  int srcId,               /* Artifact that contains this tag */
  double mtime,            /* Timestamp.  Use default if <=0.0 */
  int rid                  /* Artifact to which the tag is to attached */
){
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
    tagid, rid
  );
  db_bind_double(&s, ":mtime", mtime);
  rc = db_step(&s);
  db_finalize(&s);
  if( rc==SQLITE_ROW ){
    /* Another entry that is more recent already exists.  Do nothing */
    return;
  }
  db_prepare(&s, 
    "REPLACE INTO tagxref(tagid,tagtype,srcId,origid,value,mtime,rid)"
    " VALUES(%d,%d,%d,%d,%Q,:mtime,%d)",
    tagid, tagtype, srcId, rid, zValue, rid
  );
  db_bind_double(&s, ":mtime", mtime);







|







168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
    tagid, rid
  );
  db_bind_double(&s, ":mtime", mtime);
  rc = db_step(&s);
  db_finalize(&s);
  if( rc==SQLITE_ROW ){
    /* Another entry that is more recent already exists.  Do nothing */
    return tagid;
  }
  db_prepare(&s, 
    "REPLACE INTO tagxref(tagid,tagtype,srcId,origid,value,mtime,rid)"
    " VALUES(%d,%d,%d,%d,%Q,:mtime,%d)",
    tagid, tagtype, srcId, rid, zValue, rid
  );
  db_bind_double(&s, ":mtime", mtime);
206
207
208
209
210
211
212

213
214
215
216
217
218
219
  if( tagid==TAG_DATE ){
    db_multi_exec("UPDATE event SET mtime=julianday(%Q) WHERE objid=%d",
                  zValue, rid);
  }
  if( tagtype==0 || tagtype==2 ){
    tag_propagate(rid, tagid, tagtype, rid, zValue, mtime);
  }

}


/*
** COMMAND: test-tag
** %fossil test-tag (+|*|-)TAGNAME ARTIFACT-ID ?VALUE?
**







>







206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
  if( tagid==TAG_DATE ){
    db_multi_exec("UPDATE event SET mtime=julianday(%Q) WHERE objid=%d",
                  zValue, rid);
  }
  if( tagtype==0 || tagtype==2 ){
    tag_propagate(rid, tagid, tagtype, rid, zValue, mtime);
  }
  return tagid;
}


/*
** COMMAND: test-tag
** %fossil test-tag (+|*|-)TAGNAME ARTIFACT-ID ?VALUE?
**
Changes to src/tkt.c.
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
** TICKET table entry if createFlag is true.  If createFlag is false,
** that means we already know the entry exists and so we can save the
** work of trying to create it.
**
** Return TRUE if a new TICKET entry was created and FALSE if an
** existing entry was revised.
*/
int ticket_insert(Manifest *p, int createFlag, int checkTime){
  Blob sql;
  Stmt q;
  int i;
  const char *zSep;
  int rc = 0;

  getAllTicketFields();







|







195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
** TICKET table entry if createFlag is true.  If createFlag is false,
** that means we already know the entry exists and so we can save the
** work of trying to create it.
**
** Return TRUE if a new TICKET entry was created and FALSE if an
** existing entry was revised.
*/
int ticket_insert(const Manifest *p, int createFlag, int checkTime){
  Blob sql;
  Stmt q;
  int i;
  const char *zSep;
  int rc = 0;

  getAllTicketFields();
262
263
264
265
266
267
268

269
270
271
272
273
274
275
  );
  db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid);
  while( db_step(&q)==SQLITE_ROW ){
    int rid = db_column_int(&q, 0);
    content_get(rid, &content);
    manifest_parse(&manifest, &content);
    ticket_insert(&manifest, createFlag, 0);

    manifest_clear(&manifest);
    createFlag = 0;
  }
  db_finalize(&q);
}

/*







>







262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
  );
  db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid);
  while( db_step(&q)==SQLITE_ROW ){
    int rid = db_column_int(&q, 0);
    content_get(rid, &content);
    manifest_parse(&manifest, &content);
    ticket_insert(&manifest, createFlag, 0);
    manifest_ticket_event(rid, &manifest, createFlag);
    manifest_clear(&manifest);
    createFlag = 0;
  }
  db_finalize(&q);
}

/*
454
455
456
457
458
459
460

461

462
463
464
465
466
467
468
             "}<br />\n",
       blob_str(&tktchng));
  }else{
    rid = content_put(&tktchng, 0, 0);
    if( rid==0 ){
      fossil_panic("trouble committing ticket: %s", g.zErrMsg);
    }

    manifest_crosslink(rid, &tktchng);

  }
  return TH_RETURN;
}


/*
** WEBPAGE: tktnew







>

>







455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
             "}<br />\n",
       blob_str(&tktchng));
  }else{
    rid = content_put(&tktchng, 0, 0);
    if( rid==0 ){
      fossil_panic("trouble committing ticket: %s", g.zErrMsg);
    }
    manifest_crosslink_begin();
    manifest_crosslink(rid, &tktchng);
    manifest_crosslink_end();
  }
  return TH_RETURN;
}


/*
** WEBPAGE: tktnew
688
689
690
691
692
693
694
695

696
697
698
699
700
701
702
    if( manifest_parse(&m, &content) && m.type==CFTYPE_TICKET ){
      char *zDate = db_text(0, "SELECT datetime(%.12f)", m.rDate);
      char zUuid[12];
      memcpy(zUuid, zChngUuid, 10);
      zUuid[10] = 0;
      @
      @ Ticket change
      @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zUuid)</a>]</a> by

      hyperlink_to_user(m.zUser,zDate," on");
      hyperlink_to_date(zDate, ":");
      free(zDate);
      ticket_output_change_artifact(&m);
    }
    manifest_clear(&m);
  }







|
>







691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
    if( manifest_parse(&m, &content) && m.type==CFTYPE_TICKET ){
      char *zDate = db_text(0, "SELECT datetime(%.12f)", m.rDate);
      char zUuid[12];
      memcpy(zUuid, zChngUuid, 10);
      zUuid[10] = 0;
      @
      @ Ticket change
      @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zUuid)</a>]</a>
      @ (rid %d(rid)) by
      hyperlink_to_user(m.zUser,zDate," on");
      hyperlink_to_date(zDate, ":");
      free(zDate);
      ticket_output_change_artifact(&m);
    }
    manifest_clear(&m);
  }
Changes to src/xfer.c.
578
579
580
581
582
583
584

585
586
587
588
589
590
591
  xfer.mxSend = db_get_int("max-download", 5000000);
  g.xferPanic = 1;

  db_begin_transaction();
  db_multi_exec(
     "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
  );

  while( blob_line(xfer.pIn, &xfer.line) ){
    if( blob_buffer(&xfer.line)[0]=='#' ) continue;
    xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));

    /*   file UUID SIZE \n CONTENT
    **   file UUID DELTASRC SIZE \n CONTENT
    **







>







578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
  xfer.mxSend = db_get_int("max-download", 5000000);
  g.xferPanic = 1;

  db_begin_transaction();
  db_multi_exec(
     "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
  );
  manifest_crosslink_begin();
  while( blob_line(xfer.pIn, &xfer.line) ){
    if( blob_buffer(&xfer.line)[0]=='#' ) continue;
    xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));

    /*   file UUID SIZE \n CONTENT
    **   file UUID DELTASRC SIZE \n CONTENT
    **
829
830
831
832
833
834
835

836
837
838
839
840
841
842
  }else if( isPull ){
    create_cluster();
    send_unclustered(&xfer);
  }
  if( recvConfig ){
    configure_finalize_receive();
  }

  db_end_transaction(0);
}

/*
** COMMAND: test-xfer
**
** This command is used for debugging the server.  There is a single







>







830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
  }else if( isPull ){
    create_cluster();
    send_unclustered(&xfer);
  }
  if( recvConfig ){
    configure_finalize_receive();
  }
  manifest_crosslink_end();
  db_end_transaction(0);
}

/*
** COMMAND: test-xfer
**
** This command is used for debugging the server.  There is a single
944
945
946
947
948
949
950

951
952
953
954
955
956
957
    blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
    nCard++;
  }
  if( pushFlag ){
    blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
    nCard++;
  }

  printf(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");

  while( go ){
    int newPhantom = 0;
    char *zRandomness;

    /* Send make the most recently received cookie.  Let the server







>







946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
    blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
    nCard++;
  }
  if( pushFlag ){
    blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
    nCard++;
  }
  manifest_crosslink_begin();
  printf(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");

  while( go ){
    int newPhantom = 0;
    char *zRandomness;

    /* Send make the most recently received cookie.  Let the server
1226
1227
1228
1229
1230
1231
1232

1233
1234
  };
  transport_stats(&nSent, &nRcvd, 1);
  printf("Total network traffic: %d bytes sent, %d bytes received\n",
         nSent, nRcvd);
  transport_close();
  socket_global_shutdown();
  db_multi_exec("DROP TABLE onremote");

  db_end_transaction(0);
}







>


1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
  };
  transport_stats(&nSent, &nRcvd, 1);
  printf("Total network traffic: %d bytes sent, %d bytes received\n",
         nSent, nRcvd);
  transport_close();
  socket_global_shutdown();
  db_multi_exec("DROP TABLE onremote");
  manifest_crosslink_end();
  db_end_transaction(0);
}