Fossil SCM

Make it possible to store similar ticket change artifacts as deltas. This might be useful when a certain column of the <var>TICKET</var> table holds a lengthy text that may undergo frequent modifications. This is an opt-in feature. It is activated only when <var>TICKET</var> table contains a phony <code>INTEGER</code> column <code>"baseline for $name"</code> where <code>$name</code> stands for the name of the actual column provisioned for the aforementioned frequently changing text.

george 2022-08-22 18:27 trunk
Commit 0f4a0fe82a3f80b29790f540c60ce94f85a997f3b3bc02ae6dd84e82fe0ea2e4
1 file changed +70 -11
+70 -11
--- src/tkt.c
+++ src/tkt.c
@@ -30,22 +30,27 @@
3030
static int nField = 0;
3131
static struct tktFieldInfo {
3232
char *zName; /* Name of the database field */
3333
char *zValue; /* Value to store */
3434
char *zAppend; /* Value to append */
35
+ char *zBsln; /* "baseline for $zName" if that field exists*/
3536
unsigned mUsed; /* 01: TICKET 02: TICKETCHNG */
3637
} *aField;
3738
#define USEDBY_TICKET 01
3839
#define USEDBY_TICKETCHNG 02
3940
#define USEDBY_BOTH 03
41
+#define JCARD_ASSIGN ('=')
42
+#define JCARD_APPEND ('+')
43
+#define JCARD_PRIVATE ('p')
4044
static u8 haveTicket = 0; /* True if the TICKET table exists */
4145
static u8 haveTicketCTime = 0; /* True if TICKET.TKT_CTIME exists */
4246
static u8 haveTicketChng = 0; /* True if the TICKETCHNG table exists */
4347
static u8 haveTicketChngRid = 0; /* True if TICKETCHNG.TKT_RID exists */
4448
static u8 haveTicketChngUser = 0;/* True if TICKETCHNG.TKT_USER exists */
4549
static u8 useTicketGenMt = 0; /* use generated TICKET.MIMETYPE */
4650
static u8 useTicketChngGenMt = 0;/* use generated TICKETCHNG.MIMETYPE */
51
+static int nTicketBslns = 0; /* number of valid "baseline for ..." */
4752
4853
4954
/*
5055
** Compare two entries in aField[] for sorting purposes
5156
*/
@@ -73,31 +78,52 @@
7378
** The haveTicket and haveTicketChng variables are set to 1 if the TICKET and
7479
** TICKETCHANGE tables exist, respectively.
7580
*/
7681
static void getAllTicketFields(void){
7782
Stmt q;
78
- int i, noRegularMimetype;
83
+ int i, noRegularMimetype, noBaselines;
7984
static int once = 0;
8085
if( once ) return;
81
- once = 1;
86
+ once = noBaselines = 1;
8287
db_prepare(&q, "PRAGMA table_info(ticket)");
8388
while( db_step(&q)==SQLITE_ROW ){
8489
const char *zFieldName = db_column_text(&q, 1);
8590
haveTicket = 1;
8691
if( memcmp(zFieldName,"tkt_",4)==0 ){
8792
if( strcmp(zFieldName, "tkt_ctime")==0 ) haveTicketCTime = 1;
8893
continue;
94
+ }
95
+ if( noBaselines && memcmp(zFieldName,"baseline for ",13)==0 ){
96
+ noBaselines = 0;
97
+ continue;
8998
}
9099
if( strchr(zFieldName,' ')!=0 ) continue;
91100
if( nField%10==0 ){
92101
aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) );
93102
}
103
+ aField[nField].zBsln = 0;
94104
aField[nField].zName = mprintf("%s", zFieldName);
95105
aField[nField].mUsed = USEDBY_TICKET;
96106
nField++;
97107
}
98108
db_finalize(&q);
109
+ if( !noBaselines ){
110
+ db_prepare(&q, "SELECT 1 FROM pragma_table_info('ticket') "
111
+ "WHERE type = 'INTEGER' AND name = :n");
112
+ for(i=0; i<nField; i++){
113
+ char *zBsln = mprintf("baseline for %s",aField[i].zName);
114
+ db_bind_text(&q, ":n", zBsln);
115
+ if( db_step(&q)==SQLITE_ROW ){
116
+ aField[i].zBsln = zBsln;
117
+ nTicketBslns++;
118
+ }else{
119
+ free(zBsln);
120
+ }
121
+ db_reset(&q);
122
+ }
123
+ db_finalize(&q);
124
+ }
99125
db_prepare(&q, "PRAGMA table_info(ticketchng)");
100126
while( db_step(&q)==SQLITE_ROW ){
101127
const char *zFieldName = db_column_text(&q, 1);
102128
haveTicketChng = 1;
103129
if( memcmp(zFieldName,"tkt_",4)==0 ){
@@ -114,10 +140,11 @@
114140
continue;
115141
}
116142
if( nField%10==0 ){
117143
aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) );
118144
}
145
+ aField[nField].zBsln = 0;
119146
aField[nField].zName = mprintf("%s", zFieldName);
120147
aField[nField].mUsed = USEDBY_TICKETCHNG;
121148
nField++;
122149
}
123150
db_finalize(&q);
@@ -232,12 +259,11 @@
232259
blob_zero(&sql3);
233260
blob_append_sql(&sql1, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime");
234261
if( haveTicketCTime ){
235262
blob_append_sql(&sql1, ", tkt_ctime=coalesce(tkt_ctime,:mtime)");
236263
}
237
- aUsed = fossil_malloc( nField );
238
- memset(aUsed, 0, nField);
264
+ aUsed = fossil_malloc_zero( nField );
239265
for(i=0; i<p->nField; i++){
240266
const char * const zName = p->aField[i].zName;
241267
const char * const zBaseName = zName[0]=='+' ? zName+1 : zName;
242268
j = fieldId(zBaseName);
243269
if( j<0 ) continue;
@@ -244,12 +270,16 @@
244270
aUsed[j] = 1;
245271
if( aField[j].mUsed & USEDBY_TICKET ){
246272
if( zName[0]=='+' ){
247273
blob_append_sql(&sql1,", \"%w\"=coalesce(\"%w\",'') || %Q",
248274
zBaseName, zBaseName, p->aField[i].zValue);
275
+ /* when appending keep "baseline for ..." unchanged */
249276
}else{
250277
blob_append_sql(&sql1,", \"%w\"=%Q", zBaseName, p->aField[i].zValue);
278
+ if( aField[j].zBsln ){
279
+ blob_append_sql(&sql1,", \"%w\"=%d", aField[j].zBsln, rid);
280
+ }
251281
}
252282
}
253283
if( aField[j].mUsed & USEDBY_TICKETCHNG ){
254284
blob_append_sql(&sql2, ",\"%w\"", zBaseName);
255285
blob_append_sql(&sql3, ",%Q", p->aField[i].zValue);
@@ -742,19 +772,35 @@
742772
** Write a ticket into the repository.
743773
*/
744774
static int ticket_put(
745775
Blob *pTicket, /* The text of the ticket change record */
746776
const char *zTktId, /* The ticket to which this change is applied */
777
+ const char *aUsed, /* Indicators for fields' modifications */
747778
int needMod /* True if moderation is needed */
748779
){
749780
int result;
750781
int rid;
751782
manifest_crosslink_begin();
752783
rid = content_put_ex(pTicket, 0, 0, 0, needMod);
753784
if( rid==0 ){
754785
fossil_fatal("trouble committing ticket: %s", g.zErrMsg);
755786
}
787
+ if( nTicketBslns ){
788
+ int i, s, buf[8], nSrc=0, *aSrc=&(buf[0]);
789
+ if( nTicketBslns > count(buf) ){
790
+ aSrc = (int*)fossil_malloc(sizeof(int)*nTicketBslns);
791
+ }
792
+ for(i=0; i<nField; i++){
793
+ if( aField[i].zBsln && aUsed[i]==JCARD_ASSIGN ){
794
+ s = db_int(0,"SELECT \"%w\" FROM ticket WHERE tkt_uuid = '%q'",
795
+ aField[i].zBsln, zTktId );
796
+ if( s > 0 ) aSrc[nSrc++] = s;
797
+ }
798
+ }
799
+ if( nSrc ) content_deltify(rid, aSrc, nSrc, 0);
800
+ if( aSrc!=&(buf[0]) ) fossil_free( aSrc );
801
+ }
756802
if( needMod ){
757803
moderation_table_create();
758804
db_multi_exec(
759805
"INSERT INTO modreq(objid, tktid) VALUES(%d,%Q)",
760806
rid, zTktId
@@ -787,14 +833,14 @@
787833
void *pUuid,
788834
int argc,
789835
const char **argv,
790836
int *argl
791837
){
792
- char *zDate;
838
+ char *zDate, *aUsed;
793839
const char *zUuid;
794840
int i;
795
- int nJ = 0;
841
+ int nJ = 0, rc = TH_OK;
796842
Blob tktchng, cksum;
797843
int needMod;
798844
799845
login_verify_csrf_secret();
800846
if( !captcha_is_correct(0) ){
@@ -804,15 +850,17 @@
804850
zUuid = (const char *)pUuid;
805851
blob_zero(&tktchng);
806852
zDate = date_in_standard_format("now");
807853
blob_appendf(&tktchng, "D %s\n", zDate);
808854
free(zDate);
855
+ aUsed = fossil_malloc_zero( nField );
809856
for(i=0; i<nField; i++){
810857
if( aField[i].zAppend ){
811858
blob_appendf(&tktchng, "J +%s %z\n", aField[i].zName,
812859
fossilize(aField[i].zAppend, -1));
813860
++nJ;
861
+ aUsed[i] = JCARD_APPEND;
814862
}
815863
}
816864
for(i=0; i<nField; i++){
817865
const char *zValue;
818866
int nValue;
@@ -825,12 +873,14 @@
825873
|| strlen(aField[i].zValue)!=nValue
826874
){
827875
if( memcmp(aField[i].zName, "private_", 8)==0 ){
828876
zValue = db_conceal(zValue, nValue);
829877
blob_appendf(&tktchng, "J %s %s\n", aField[i].zName, zValue);
878
+ aUsed[i] = JCARD_PRIVATE;
830879
}else{
831880
blob_appendf(&tktchng, "J %s %#F\n", aField[i].zName, nValue, zValue);
881
+ aUsed[i] = JCARD_ASSIGN;
832882
}
833883
nJ++;
834884
}
835885
}
836886
}
@@ -846,11 +896,11 @@
846896
blob_appendf(&tktchng, "U %F\n", login_name());
847897
md5sum_blob(&tktchng, &cksum);
848898
blob_appendf(&tktchng, "Z %b\n", &cksum);
849899
if( nJ==0 ){
850900
blob_reset(&tktchng);
851
- return TH_OK;
901
+ goto finish;
852902
}
853903
needMod = ticket_need_moderation(0);
854904
if( g.zPath[0]=='d' ){
855905
const char *zNeedMod = needMod ? "required" : "skipped";
856906
/* If called from /debug_tktnew or /debug_tktedit... */
@@ -858,20 +908,22 @@
858908
@ <p>Ticket artifact that would have been submitted:</p>
859909
@ <blockquote><pre>%h(blob_str(&tktchng))</pre></blockquote>
860910
@ <blockquote><pre>Moderation would be %h(zNeedMod).</pre></blockquote>
861911
@ </div>
862912
@ <hr />
863
- return TH_OK;
864913
}else{
865914
if( g.thTrace ){
866915
Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n"
867916
"}<br />\n",
868917
blob_str(&tktchng));
869918
}
870
- ticket_put(&tktchng, zUuid, needMod);
919
+ ticket_put(&tktchng, zUuid, aUsed, needMod);
920
+ rc = ticket_change(zUuid);
871921
}
872
- return ticket_change(zUuid);
922
+ finish:
923
+ fossil_free( aUsed );
924
+ return rc;
873925
}
874926
875927
876928
/*
877929
** WEBPAGE: tktnew
@@ -1460,10 +1512,11 @@
14601512
}else{
14611513
/* add a new ticket or update an existing ticket */
14621514
enum { set,add,history,err } eCmd = err;
14631515
int i = 0;
14641516
Blob tktchng, cksum;
1517
+ char *aUsed;
14651518
14661519
/* get command type (set/add) and get uuid, if needed for set */
14671520
if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 ||
14681521
strncmp(g.argv[2],"history",n)==0 ){
14691522
if( strncmp(g.argv[2],"history",n)==0 ){
@@ -1602,10 +1655,11 @@
16021655
}else{
16031656
aField[j].zValue = zFValue;
16041657
}
16051658
}
16061659
}
1660
+ aUsed = fossil_malloc_zero( nField );
16071661
16081662
/* now add the needed artifacts to the repository */
16091663
blob_zero(&tktchng);
16101664
/* add the time to the ticket manifest */
16111665
blob_appendf(&tktchng, "D %s\n", zDate);
@@ -1615,34 +1669,39 @@
16151669
char *zPfx;
16161670
16171671
if( aField[i].zAppend && aField[i].zAppend[0] ){
16181672
zPfx = " +";
16191673
zValue = aField[i].zAppend;
1674
+ aUsed[i] = JCARD_APPEND;
16201675
}else if( aField[i].zValue && aField[i].zValue[0] ){
16211676
zPfx = " ";
16221677
zValue = aField[i].zValue;
1678
+ aUsed[i] = JCARD_ASSIGN;
16231679
}else{
16241680
continue;
16251681
}
16261682
if( memcmp(aField[i].zName, "private_", 8)==0 ){
16271683
zValue = db_conceal(zValue, strlen(zValue));
16281684
blob_appendf(&tktchng, "J%s%s %s\n", zPfx, aField[i].zName, zValue);
1685
+ aUsed[i] = JCARD_PRIVATE;
16291686
}else{
16301687
blob_appendf(&tktchng, "J%s%s %#F\n", zPfx,
16311688
aField[i].zName, strlen(zValue), zValue);
16321689
}
16331690
}
16341691
blob_appendf(&tktchng, "K %s\n", zTktUuid);
16351692
blob_appendf(&tktchng, "U %F\n", zUser);
16361693
md5sum_blob(&tktchng, &cksum);
16371694
blob_appendf(&tktchng, "Z %b\n", &cksum);
1638
- if( ticket_put(&tktchng, zTktUuid, ticket_need_moderation(1))==0 ){
1695
+ if( ticket_put(&tktchng, zTktUuid, aUsed,
1696
+ ticket_need_moderation(1) )==0 ){
16391697
fossil_fatal("%s", g.zErrMsg);
16401698
}else{
16411699
fossil_print("ticket %s succeeded for %s\n",
16421700
(eCmd==set?"set":"add"),zTktUuid);
16431701
}
1702
+ fossil_free( aUsed );
16441703
}
16451704
}
16461705
}
16471706
16481707
16491708
--- src/tkt.c
+++ src/tkt.c
@@ -30,22 +30,27 @@
30 static int nField = 0;
31 static struct tktFieldInfo {
32 char *zName; /* Name of the database field */
33 char *zValue; /* Value to store */
34 char *zAppend; /* Value to append */
 
35 unsigned mUsed; /* 01: TICKET 02: TICKETCHNG */
36 } *aField;
37 #define USEDBY_TICKET 01
38 #define USEDBY_TICKETCHNG 02
39 #define USEDBY_BOTH 03
 
 
 
40 static u8 haveTicket = 0; /* True if the TICKET table exists */
41 static u8 haveTicketCTime = 0; /* True if TICKET.TKT_CTIME exists */
42 static u8 haveTicketChng = 0; /* True if the TICKETCHNG table exists */
43 static u8 haveTicketChngRid = 0; /* True if TICKETCHNG.TKT_RID exists */
44 static u8 haveTicketChngUser = 0;/* True if TICKETCHNG.TKT_USER exists */
45 static u8 useTicketGenMt = 0; /* use generated TICKET.MIMETYPE */
46 static u8 useTicketChngGenMt = 0;/* use generated TICKETCHNG.MIMETYPE */
 
47
48
49 /*
50 ** Compare two entries in aField[] for sorting purposes
51 */
@@ -73,31 +78,52 @@
73 ** The haveTicket and haveTicketChng variables are set to 1 if the TICKET and
74 ** TICKETCHANGE tables exist, respectively.
75 */
76 static void getAllTicketFields(void){
77 Stmt q;
78 int i, noRegularMimetype;
79 static int once = 0;
80 if( once ) return;
81 once = 1;
82 db_prepare(&q, "PRAGMA table_info(ticket)");
83 while( db_step(&q)==SQLITE_ROW ){
84 const char *zFieldName = db_column_text(&q, 1);
85 haveTicket = 1;
86 if( memcmp(zFieldName,"tkt_",4)==0 ){
87 if( strcmp(zFieldName, "tkt_ctime")==0 ) haveTicketCTime = 1;
88 continue;
 
 
 
 
89 }
90 if( strchr(zFieldName,' ')!=0 ) continue;
91 if( nField%10==0 ){
92 aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) );
93 }
 
94 aField[nField].zName = mprintf("%s", zFieldName);
95 aField[nField].mUsed = USEDBY_TICKET;
96 nField++;
97 }
98 db_finalize(&q);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99 db_prepare(&q, "PRAGMA table_info(ticketchng)");
100 while( db_step(&q)==SQLITE_ROW ){
101 const char *zFieldName = db_column_text(&q, 1);
102 haveTicketChng = 1;
103 if( memcmp(zFieldName,"tkt_",4)==0 ){
@@ -114,10 +140,11 @@
114 continue;
115 }
116 if( nField%10==0 ){
117 aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) );
118 }
 
119 aField[nField].zName = mprintf("%s", zFieldName);
120 aField[nField].mUsed = USEDBY_TICKETCHNG;
121 nField++;
122 }
123 db_finalize(&q);
@@ -232,12 +259,11 @@
232 blob_zero(&sql3);
233 blob_append_sql(&sql1, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime");
234 if( haveTicketCTime ){
235 blob_append_sql(&sql1, ", tkt_ctime=coalesce(tkt_ctime,:mtime)");
236 }
237 aUsed = fossil_malloc( nField );
238 memset(aUsed, 0, nField);
239 for(i=0; i<p->nField; i++){
240 const char * const zName = p->aField[i].zName;
241 const char * const zBaseName = zName[0]=='+' ? zName+1 : zName;
242 j = fieldId(zBaseName);
243 if( j<0 ) continue;
@@ -244,12 +270,16 @@
244 aUsed[j] = 1;
245 if( aField[j].mUsed & USEDBY_TICKET ){
246 if( zName[0]=='+' ){
247 blob_append_sql(&sql1,", \"%w\"=coalesce(\"%w\",'') || %Q",
248 zBaseName, zBaseName, p->aField[i].zValue);
 
249 }else{
250 blob_append_sql(&sql1,", \"%w\"=%Q", zBaseName, p->aField[i].zValue);
 
 
 
251 }
252 }
253 if( aField[j].mUsed & USEDBY_TICKETCHNG ){
254 blob_append_sql(&sql2, ",\"%w\"", zBaseName);
255 blob_append_sql(&sql3, ",%Q", p->aField[i].zValue);
@@ -742,19 +772,35 @@
742 ** Write a ticket into the repository.
743 */
744 static int ticket_put(
745 Blob *pTicket, /* The text of the ticket change record */
746 const char *zTktId, /* The ticket to which this change is applied */
 
747 int needMod /* True if moderation is needed */
748 ){
749 int result;
750 int rid;
751 manifest_crosslink_begin();
752 rid = content_put_ex(pTicket, 0, 0, 0, needMod);
753 if( rid==0 ){
754 fossil_fatal("trouble committing ticket: %s", g.zErrMsg);
755 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
756 if( needMod ){
757 moderation_table_create();
758 db_multi_exec(
759 "INSERT INTO modreq(objid, tktid) VALUES(%d,%Q)",
760 rid, zTktId
@@ -787,14 +833,14 @@
787 void *pUuid,
788 int argc,
789 const char **argv,
790 int *argl
791 ){
792 char *zDate;
793 const char *zUuid;
794 int i;
795 int nJ = 0;
796 Blob tktchng, cksum;
797 int needMod;
798
799 login_verify_csrf_secret();
800 if( !captcha_is_correct(0) ){
@@ -804,15 +850,17 @@
804 zUuid = (const char *)pUuid;
805 blob_zero(&tktchng);
806 zDate = date_in_standard_format("now");
807 blob_appendf(&tktchng, "D %s\n", zDate);
808 free(zDate);
 
809 for(i=0; i<nField; i++){
810 if( aField[i].zAppend ){
811 blob_appendf(&tktchng, "J +%s %z\n", aField[i].zName,
812 fossilize(aField[i].zAppend, -1));
813 ++nJ;
 
814 }
815 }
816 for(i=0; i<nField; i++){
817 const char *zValue;
818 int nValue;
@@ -825,12 +873,14 @@
825 || strlen(aField[i].zValue)!=nValue
826 ){
827 if( memcmp(aField[i].zName, "private_", 8)==0 ){
828 zValue = db_conceal(zValue, nValue);
829 blob_appendf(&tktchng, "J %s %s\n", aField[i].zName, zValue);
 
830 }else{
831 blob_appendf(&tktchng, "J %s %#F\n", aField[i].zName, nValue, zValue);
 
832 }
833 nJ++;
834 }
835 }
836 }
@@ -846,11 +896,11 @@
846 blob_appendf(&tktchng, "U %F\n", login_name());
847 md5sum_blob(&tktchng, &cksum);
848 blob_appendf(&tktchng, "Z %b\n", &cksum);
849 if( nJ==0 ){
850 blob_reset(&tktchng);
851 return TH_OK;
852 }
853 needMod = ticket_need_moderation(0);
854 if( g.zPath[0]=='d' ){
855 const char *zNeedMod = needMod ? "required" : "skipped";
856 /* If called from /debug_tktnew or /debug_tktedit... */
@@ -858,20 +908,22 @@
858 @ <p>Ticket artifact that would have been submitted:</p>
859 @ <blockquote><pre>%h(blob_str(&tktchng))</pre></blockquote>
860 @ <blockquote><pre>Moderation would be %h(zNeedMod).</pre></blockquote>
861 @ </div>
862 @ <hr />
863 return TH_OK;
864 }else{
865 if( g.thTrace ){
866 Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n"
867 "}<br />\n",
868 blob_str(&tktchng));
869 }
870 ticket_put(&tktchng, zUuid, needMod);
 
871 }
872 return ticket_change(zUuid);
 
 
873 }
874
875
876 /*
877 ** WEBPAGE: tktnew
@@ -1460,10 +1512,11 @@
1460 }else{
1461 /* add a new ticket or update an existing ticket */
1462 enum { set,add,history,err } eCmd = err;
1463 int i = 0;
1464 Blob tktchng, cksum;
 
1465
1466 /* get command type (set/add) and get uuid, if needed for set */
1467 if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 ||
1468 strncmp(g.argv[2],"history",n)==0 ){
1469 if( strncmp(g.argv[2],"history",n)==0 ){
@@ -1602,10 +1655,11 @@
1602 }else{
1603 aField[j].zValue = zFValue;
1604 }
1605 }
1606 }
 
1607
1608 /* now add the needed artifacts to the repository */
1609 blob_zero(&tktchng);
1610 /* add the time to the ticket manifest */
1611 blob_appendf(&tktchng, "D %s\n", zDate);
@@ -1615,34 +1669,39 @@
1615 char *zPfx;
1616
1617 if( aField[i].zAppend && aField[i].zAppend[0] ){
1618 zPfx = " +";
1619 zValue = aField[i].zAppend;
 
1620 }else if( aField[i].zValue && aField[i].zValue[0] ){
1621 zPfx = " ";
1622 zValue = aField[i].zValue;
 
1623 }else{
1624 continue;
1625 }
1626 if( memcmp(aField[i].zName, "private_", 8)==0 ){
1627 zValue = db_conceal(zValue, strlen(zValue));
1628 blob_appendf(&tktchng, "J%s%s %s\n", zPfx, aField[i].zName, zValue);
 
1629 }else{
1630 blob_appendf(&tktchng, "J%s%s %#F\n", zPfx,
1631 aField[i].zName, strlen(zValue), zValue);
1632 }
1633 }
1634 blob_appendf(&tktchng, "K %s\n", zTktUuid);
1635 blob_appendf(&tktchng, "U %F\n", zUser);
1636 md5sum_blob(&tktchng, &cksum);
1637 blob_appendf(&tktchng, "Z %b\n", &cksum);
1638 if( ticket_put(&tktchng, zTktUuid, ticket_need_moderation(1))==0 ){
 
1639 fossil_fatal("%s", g.zErrMsg);
1640 }else{
1641 fossil_print("ticket %s succeeded for %s\n",
1642 (eCmd==set?"set":"add"),zTktUuid);
1643 }
 
1644 }
1645 }
1646 }
1647
1648
1649
--- src/tkt.c
+++ src/tkt.c
@@ -30,22 +30,27 @@
30 static int nField = 0;
31 static struct tktFieldInfo {
32 char *zName; /* Name of the database field */
33 char *zValue; /* Value to store */
34 char *zAppend; /* Value to append */
35 char *zBsln; /* "baseline for $zName" if that field exists*/
36 unsigned mUsed; /* 01: TICKET 02: TICKETCHNG */
37 } *aField;
38 #define USEDBY_TICKET 01
39 #define USEDBY_TICKETCHNG 02
40 #define USEDBY_BOTH 03
41 #define JCARD_ASSIGN ('=')
42 #define JCARD_APPEND ('+')
43 #define JCARD_PRIVATE ('p')
44 static u8 haveTicket = 0; /* True if the TICKET table exists */
45 static u8 haveTicketCTime = 0; /* True if TICKET.TKT_CTIME exists */
46 static u8 haveTicketChng = 0; /* True if the TICKETCHNG table exists */
47 static u8 haveTicketChngRid = 0; /* True if TICKETCHNG.TKT_RID exists */
48 static u8 haveTicketChngUser = 0;/* True if TICKETCHNG.TKT_USER exists */
49 static u8 useTicketGenMt = 0; /* use generated TICKET.MIMETYPE */
50 static u8 useTicketChngGenMt = 0;/* use generated TICKETCHNG.MIMETYPE */
51 static int nTicketBslns = 0; /* number of valid "baseline for ..." */
52
53
54 /*
55 ** Compare two entries in aField[] for sorting purposes
56 */
@@ -73,31 +78,52 @@
78 ** The haveTicket and haveTicketChng variables are set to 1 if the TICKET and
79 ** TICKETCHANGE tables exist, respectively.
80 */
81 static void getAllTicketFields(void){
82 Stmt q;
83 int i, noRegularMimetype, noBaselines;
84 static int once = 0;
85 if( once ) return;
86 once = noBaselines = 1;
87 db_prepare(&q, "PRAGMA table_info(ticket)");
88 while( db_step(&q)==SQLITE_ROW ){
89 const char *zFieldName = db_column_text(&q, 1);
90 haveTicket = 1;
91 if( memcmp(zFieldName,"tkt_",4)==0 ){
92 if( strcmp(zFieldName, "tkt_ctime")==0 ) haveTicketCTime = 1;
93 continue;
94 }
95 if( noBaselines && memcmp(zFieldName,"baseline for ",13)==0 ){
96 noBaselines = 0;
97 continue;
98 }
99 if( strchr(zFieldName,' ')!=0 ) continue;
100 if( nField%10==0 ){
101 aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) );
102 }
103 aField[nField].zBsln = 0;
104 aField[nField].zName = mprintf("%s", zFieldName);
105 aField[nField].mUsed = USEDBY_TICKET;
106 nField++;
107 }
108 db_finalize(&q);
109 if( !noBaselines ){
110 db_prepare(&q, "SELECT 1 FROM pragma_table_info('ticket') "
111 "WHERE type = 'INTEGER' AND name = :n");
112 for(i=0; i<nField; i++){
113 char *zBsln = mprintf("baseline for %s",aField[i].zName);
114 db_bind_text(&q, ":n", zBsln);
115 if( db_step(&q)==SQLITE_ROW ){
116 aField[i].zBsln = zBsln;
117 nTicketBslns++;
118 }else{
119 free(zBsln);
120 }
121 db_reset(&q);
122 }
123 db_finalize(&q);
124 }
125 db_prepare(&q, "PRAGMA table_info(ticketchng)");
126 while( db_step(&q)==SQLITE_ROW ){
127 const char *zFieldName = db_column_text(&q, 1);
128 haveTicketChng = 1;
129 if( memcmp(zFieldName,"tkt_",4)==0 ){
@@ -114,10 +140,11 @@
140 continue;
141 }
142 if( nField%10==0 ){
143 aField = fossil_realloc(aField, sizeof(aField[0])*(nField+10) );
144 }
145 aField[nField].zBsln = 0;
146 aField[nField].zName = mprintf("%s", zFieldName);
147 aField[nField].mUsed = USEDBY_TICKETCHNG;
148 nField++;
149 }
150 db_finalize(&q);
@@ -232,12 +259,11 @@
259 blob_zero(&sql3);
260 blob_append_sql(&sql1, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime");
261 if( haveTicketCTime ){
262 blob_append_sql(&sql1, ", tkt_ctime=coalesce(tkt_ctime,:mtime)");
263 }
264 aUsed = fossil_malloc_zero( nField );
 
265 for(i=0; i<p->nField; i++){
266 const char * const zName = p->aField[i].zName;
267 const char * const zBaseName = zName[0]=='+' ? zName+1 : zName;
268 j = fieldId(zBaseName);
269 if( j<0 ) continue;
@@ -244,12 +270,16 @@
270 aUsed[j] = 1;
271 if( aField[j].mUsed & USEDBY_TICKET ){
272 if( zName[0]=='+' ){
273 blob_append_sql(&sql1,", \"%w\"=coalesce(\"%w\",'') || %Q",
274 zBaseName, zBaseName, p->aField[i].zValue);
275 /* when appending keep "baseline for ..." unchanged */
276 }else{
277 blob_append_sql(&sql1,", \"%w\"=%Q", zBaseName, p->aField[i].zValue);
278 if( aField[j].zBsln ){
279 blob_append_sql(&sql1,", \"%w\"=%d", aField[j].zBsln, rid);
280 }
281 }
282 }
283 if( aField[j].mUsed & USEDBY_TICKETCHNG ){
284 blob_append_sql(&sql2, ",\"%w\"", zBaseName);
285 blob_append_sql(&sql3, ",%Q", p->aField[i].zValue);
@@ -742,19 +772,35 @@
772 ** Write a ticket into the repository.
773 */
774 static int ticket_put(
775 Blob *pTicket, /* The text of the ticket change record */
776 const char *zTktId, /* The ticket to which this change is applied */
777 const char *aUsed, /* Indicators for fields' modifications */
778 int needMod /* True if moderation is needed */
779 ){
780 int result;
781 int rid;
782 manifest_crosslink_begin();
783 rid = content_put_ex(pTicket, 0, 0, 0, needMod);
784 if( rid==0 ){
785 fossil_fatal("trouble committing ticket: %s", g.zErrMsg);
786 }
787 if( nTicketBslns ){
788 int i, s, buf[8], nSrc=0, *aSrc=&(buf[0]);
789 if( nTicketBslns > count(buf) ){
790 aSrc = (int*)fossil_malloc(sizeof(int)*nTicketBslns);
791 }
792 for(i=0; i<nField; i++){
793 if( aField[i].zBsln && aUsed[i]==JCARD_ASSIGN ){
794 s = db_int(0,"SELECT \"%w\" FROM ticket WHERE tkt_uuid = '%q'",
795 aField[i].zBsln, zTktId );
796 if( s > 0 ) aSrc[nSrc++] = s;
797 }
798 }
799 if( nSrc ) content_deltify(rid, aSrc, nSrc, 0);
800 if( aSrc!=&(buf[0]) ) fossil_free( aSrc );
801 }
802 if( needMod ){
803 moderation_table_create();
804 db_multi_exec(
805 "INSERT INTO modreq(objid, tktid) VALUES(%d,%Q)",
806 rid, zTktId
@@ -787,14 +833,14 @@
833 void *pUuid,
834 int argc,
835 const char **argv,
836 int *argl
837 ){
838 char *zDate, *aUsed;
839 const char *zUuid;
840 int i;
841 int nJ = 0, rc = TH_OK;
842 Blob tktchng, cksum;
843 int needMod;
844
845 login_verify_csrf_secret();
846 if( !captcha_is_correct(0) ){
@@ -804,15 +850,17 @@
850 zUuid = (const char *)pUuid;
851 blob_zero(&tktchng);
852 zDate = date_in_standard_format("now");
853 blob_appendf(&tktchng, "D %s\n", zDate);
854 free(zDate);
855 aUsed = fossil_malloc_zero( nField );
856 for(i=0; i<nField; i++){
857 if( aField[i].zAppend ){
858 blob_appendf(&tktchng, "J +%s %z\n", aField[i].zName,
859 fossilize(aField[i].zAppend, -1));
860 ++nJ;
861 aUsed[i] = JCARD_APPEND;
862 }
863 }
864 for(i=0; i<nField; i++){
865 const char *zValue;
866 int nValue;
@@ -825,12 +873,14 @@
873 || strlen(aField[i].zValue)!=nValue
874 ){
875 if( memcmp(aField[i].zName, "private_", 8)==0 ){
876 zValue = db_conceal(zValue, nValue);
877 blob_appendf(&tktchng, "J %s %s\n", aField[i].zName, zValue);
878 aUsed[i] = JCARD_PRIVATE;
879 }else{
880 blob_appendf(&tktchng, "J %s %#F\n", aField[i].zName, nValue, zValue);
881 aUsed[i] = JCARD_ASSIGN;
882 }
883 nJ++;
884 }
885 }
886 }
@@ -846,11 +896,11 @@
896 blob_appendf(&tktchng, "U %F\n", login_name());
897 md5sum_blob(&tktchng, &cksum);
898 blob_appendf(&tktchng, "Z %b\n", &cksum);
899 if( nJ==0 ){
900 blob_reset(&tktchng);
901 goto finish;
902 }
903 needMod = ticket_need_moderation(0);
904 if( g.zPath[0]=='d' ){
905 const char *zNeedMod = needMod ? "required" : "skipped";
906 /* If called from /debug_tktnew or /debug_tktedit... */
@@ -858,20 +908,22 @@
908 @ <p>Ticket artifact that would have been submitted:</p>
909 @ <blockquote><pre>%h(blob_str(&tktchng))</pre></blockquote>
910 @ <blockquote><pre>Moderation would be %h(zNeedMod).</pre></blockquote>
911 @ </div>
912 @ <hr />
 
913 }else{
914 if( g.thTrace ){
915 Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n"
916 "}<br />\n",
917 blob_str(&tktchng));
918 }
919 ticket_put(&tktchng, zUuid, aUsed, needMod);
920 rc = ticket_change(zUuid);
921 }
922 finish:
923 fossil_free( aUsed );
924 return rc;
925 }
926
927
928 /*
929 ** WEBPAGE: tktnew
@@ -1460,10 +1512,11 @@
1512 }else{
1513 /* add a new ticket or update an existing ticket */
1514 enum { set,add,history,err } eCmd = err;
1515 int i = 0;
1516 Blob tktchng, cksum;
1517 char *aUsed;
1518
1519 /* get command type (set/add) and get uuid, if needed for set */
1520 if( strncmp(g.argv[2],"set",n)==0 || strncmp(g.argv[2],"change",n)==0 ||
1521 strncmp(g.argv[2],"history",n)==0 ){
1522 if( strncmp(g.argv[2],"history",n)==0 ){
@@ -1602,10 +1655,11 @@
1655 }else{
1656 aField[j].zValue = zFValue;
1657 }
1658 }
1659 }
1660 aUsed = fossil_malloc_zero( nField );
1661
1662 /* now add the needed artifacts to the repository */
1663 blob_zero(&tktchng);
1664 /* add the time to the ticket manifest */
1665 blob_appendf(&tktchng, "D %s\n", zDate);
@@ -1615,34 +1669,39 @@
1669 char *zPfx;
1670
1671 if( aField[i].zAppend && aField[i].zAppend[0] ){
1672 zPfx = " +";
1673 zValue = aField[i].zAppend;
1674 aUsed[i] = JCARD_APPEND;
1675 }else if( aField[i].zValue && aField[i].zValue[0] ){
1676 zPfx = " ";
1677 zValue = aField[i].zValue;
1678 aUsed[i] = JCARD_ASSIGN;
1679 }else{
1680 continue;
1681 }
1682 if( memcmp(aField[i].zName, "private_", 8)==0 ){
1683 zValue = db_conceal(zValue, strlen(zValue));
1684 blob_appendf(&tktchng, "J%s%s %s\n", zPfx, aField[i].zName, zValue);
1685 aUsed[i] = JCARD_PRIVATE;
1686 }else{
1687 blob_appendf(&tktchng, "J%s%s %#F\n", zPfx,
1688 aField[i].zName, strlen(zValue), zValue);
1689 }
1690 }
1691 blob_appendf(&tktchng, "K %s\n", zTktUuid);
1692 blob_appendf(&tktchng, "U %F\n", zUser);
1693 md5sum_blob(&tktchng, &cksum);
1694 blob_appendf(&tktchng, "Z %b\n", &cksum);
1695 if( ticket_put(&tktchng, zTktUuid, aUsed,
1696 ticket_need_moderation(1) )==0 ){
1697 fossil_fatal("%s", g.zErrMsg);
1698 }else{
1699 fossil_print("ticket %s succeeded for %s\n",
1700 (eCmd==set?"set":"add"),zTktUuid);
1701 }
1702 fossil_free( aUsed );
1703 }
1704 }
1705 }
1706
1707
1708

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button