Fossil SCM

Extra defenses against running the digest alert generator in a context where the transaction will rollback, thus failing to record the new digest time. Change the "fossil server" and "fossil ui" commands to always log errors to the console if no other error logging is defined.

drh 2018-06-24 17:44 trunk
Commit f87fb02780a03b1d128528ad1fda9bb78030443256fd329d7979d2738a845b66
+17 -3
--- src/db.c
+++ src/db.c
@@ -133,10 +133,18 @@
133133
*/
134134
void db_delete_on_failure(const char *zFilename){
135135
assert( db.nDeleteOnFail<count(db.azDeleteOnFail) );
136136
db.azDeleteOnFail[db.nDeleteOnFail++] = fossil_strdup(zFilename);
137137
}
138
+
139
+/*
140
+** Return the transaction nesting depth. 0 means we are currently
141
+** not in a transaction.
142
+*/
143
+int db_transaction_nesting_depth(void){
144
+ return db.nBegin;
145
+}
138146
139147
/*
140148
** This routine is called by the SQLite commit-hook mechanism
141149
** just prior to each commit. All this routine does is verify
142150
** that nBegin really is zero. That insures that transactions
@@ -165,11 +173,14 @@
165173
}
166174
db.nBegin++;
167175
}
168176
void db_end_transaction(int rollbackFlag){
169177
if( g.db==0 ) return;
170
- if( db.nBegin<=0 ) return;
178
+ if( db.nBegin<=0 ){
179
+ fossil_warning("Extra call to db_end_transaction\n");
180
+ return;
181
+ }
171182
if( rollbackFlag ){
172183
db.doRollback = 1;
173184
if( g.fSqlTrace ) fossil_trace("-- ROLLBACK by request\n");
174185
}
175186
db.nBegin--;
@@ -1765,13 +1776,16 @@
17651776
fprintf(stderr, "-- prepared statements %10d\n", db.nPrepare);
17661777
}
17671778
while( db.pAllStmt ){
17681779
db_finalize(db.pAllStmt);
17691780
}
1770
- db_end_transaction(1);
1781
+ if( db.nBegin ){
1782
+ fossil_warning("Missed call to db_end_transaction(). Rolling back.\n");
1783
+ db_end_transaction(1);
1784
+ }
17711785
pStmt = 0;
1772
- g.dbIgnoreErrors++; /* Stop "database locked" warnings from PRAGMA optimize */
1786
+ g.dbIgnoreErrors++; /* Stop "database locked" warnings from PRAGMA optimize */
17731787
sqlite3_exec(g.db, "PRAGMA optimize", 0, 0, 0);
17741788
g.dbIgnoreErrors--;
17751789
db_close_config();
17761790
17771791
/* If the localdb has a lot of unused free space,
17781792
--- src/db.c
+++ src/db.c
@@ -133,10 +133,18 @@
133 */
134 void db_delete_on_failure(const char *zFilename){
135 assert( db.nDeleteOnFail<count(db.azDeleteOnFail) );
136 db.azDeleteOnFail[db.nDeleteOnFail++] = fossil_strdup(zFilename);
137 }
 
 
 
 
 
 
 
 
138
139 /*
140 ** This routine is called by the SQLite commit-hook mechanism
141 ** just prior to each commit. All this routine does is verify
142 ** that nBegin really is zero. That insures that transactions
@@ -165,11 +173,14 @@
165 }
166 db.nBegin++;
167 }
168 void db_end_transaction(int rollbackFlag){
169 if( g.db==0 ) return;
170 if( db.nBegin<=0 ) return;
 
 
 
171 if( rollbackFlag ){
172 db.doRollback = 1;
173 if( g.fSqlTrace ) fossil_trace("-- ROLLBACK by request\n");
174 }
175 db.nBegin--;
@@ -1765,13 +1776,16 @@
1765 fprintf(stderr, "-- prepared statements %10d\n", db.nPrepare);
1766 }
1767 while( db.pAllStmt ){
1768 db_finalize(db.pAllStmt);
1769 }
1770 db_end_transaction(1);
 
 
 
1771 pStmt = 0;
1772 g.dbIgnoreErrors++; /* Stop "database locked" warnings from PRAGMA optimize */
1773 sqlite3_exec(g.db, "PRAGMA optimize", 0, 0, 0);
1774 g.dbIgnoreErrors--;
1775 db_close_config();
1776
1777 /* If the localdb has a lot of unused free space,
1778
--- src/db.c
+++ src/db.c
@@ -133,10 +133,18 @@
133 */
134 void db_delete_on_failure(const char *zFilename){
135 assert( db.nDeleteOnFail<count(db.azDeleteOnFail) );
136 db.azDeleteOnFail[db.nDeleteOnFail++] = fossil_strdup(zFilename);
137 }
138
139 /*
140 ** Return the transaction nesting depth. 0 means we are currently
141 ** not in a transaction.
142 */
143 int db_transaction_nesting_depth(void){
144 return db.nBegin;
145 }
146
147 /*
148 ** This routine is called by the SQLite commit-hook mechanism
149 ** just prior to each commit. All this routine does is verify
150 ** that nBegin really is zero. That insures that transactions
@@ -165,11 +173,14 @@
173 }
174 db.nBegin++;
175 }
176 void db_end_transaction(int rollbackFlag){
177 if( g.db==0 ) return;
178 if( db.nBegin<=0 ){
179 fossil_warning("Extra call to db_end_transaction\n");
180 return;
181 }
182 if( rollbackFlag ){
183 db.doRollback = 1;
184 if( g.fSqlTrace ) fossil_trace("-- ROLLBACK by request\n");
185 }
186 db.nBegin--;
@@ -1765,13 +1776,16 @@
1776 fprintf(stderr, "-- prepared statements %10d\n", db.nPrepare);
1777 }
1778 while( db.pAllStmt ){
1779 db_finalize(db.pAllStmt);
1780 }
1781 if( db.nBegin ){
1782 fossil_warning("Missed call to db_end_transaction(). Rolling back.\n");
1783 db_end_transaction(1);
1784 }
1785 pStmt = 0;
1786 g.dbIgnoreErrors++; /* Stop "database locked" warnings from PRAGMA optimize */
1787 sqlite3_exec(g.db, "PRAGMA optimize", 0, 0, 0);
1788 g.dbIgnoreErrors--;
1789 db_close_config();
1790
1791 /* If the localdb has a lot of unused free space,
1792
+10 -7
--- src/email.c
+++ src/email.c
@@ -1772,24 +1772,27 @@
17721772
** have already responded.
17731773
*/
17741774
void email_auto_exec(void){
17751775
int iJulianDay;
17761776
if( g.db==0 ) return;
1777
+ if( db_transaction_nesting_depth()!=0 ){
1778
+ fossil_warning("Called email_auto_exec() from within a transaction");
1779
+ return;
1780
+ }
17771781
db_begin_transaction();
17781782
if( !email_tables_exist() ) goto autoexec_done;
17791783
if( !db_get_boolean("email-autoexec",0) ) goto autoexec_done;
17801784
if( !db_exists("SELECT 1 FROM pending_alert") ) goto autoexec_done;
17811785
email_send_alerts(0);
17821786
iJulianDay = db_int(0, "SELECT julianday('now')");
1783
- if( g.fSqlTrace ) fossil_trace("-- iJulianDay: %d\n", iJulianDay);
17841787
if( iJulianDay>db_get_int("email-last-digest",0) ){
1785
- db_multi_exec(
1786
- "REPLACE INTO repository.config(name,value,mtime)"
1787
- " VALUES('email-last-digest',%d,now())",
1788
- iJulianDay
1789
- );
1790
- email_send_alerts(SENDALERT_DIGEST);
1788
+ if( db_transaction_nesting_depth()!=1 ){
1789
+ fossil_warning("Transaction nesting error prior to digest processing");
1790
+ }else{
1791
+ db_set_int("email-last-digest",iJulianDay,0);
1792
+ email_send_alerts(SENDALERT_DIGEST);
1793
+ }
17911794
}
17921795
17931796
autoexec_done:
17941797
db_end_transaction(0);
17951798
}
17961799
--- src/email.c
+++ src/email.c
@@ -1772,24 +1772,27 @@
1772 ** have already responded.
1773 */
1774 void email_auto_exec(void){
1775 int iJulianDay;
1776 if( g.db==0 ) return;
 
 
 
 
1777 db_begin_transaction();
1778 if( !email_tables_exist() ) goto autoexec_done;
1779 if( !db_get_boolean("email-autoexec",0) ) goto autoexec_done;
1780 if( !db_exists("SELECT 1 FROM pending_alert") ) goto autoexec_done;
1781 email_send_alerts(0);
1782 iJulianDay = db_int(0, "SELECT julianday('now')");
1783 if( g.fSqlTrace ) fossil_trace("-- iJulianDay: %d\n", iJulianDay);
1784 if( iJulianDay>db_get_int("email-last-digest",0) ){
1785 db_multi_exec(
1786 "REPLACE INTO repository.config(name,value,mtime)"
1787 " VALUES('email-last-digest',%d,now())",
1788 iJulianDay
1789 );
1790 email_send_alerts(SENDALERT_DIGEST);
1791 }
1792
1793 autoexec_done:
1794 db_end_transaction(0);
1795 }
1796
--- src/email.c
+++ src/email.c
@@ -1772,24 +1772,27 @@
1772 ** have already responded.
1773 */
1774 void email_auto_exec(void){
1775 int iJulianDay;
1776 if( g.db==0 ) return;
1777 if( db_transaction_nesting_depth()!=0 ){
1778 fossil_warning("Called email_auto_exec() from within a transaction");
1779 return;
1780 }
1781 db_begin_transaction();
1782 if( !email_tables_exist() ) goto autoexec_done;
1783 if( !db_get_boolean("email-autoexec",0) ) goto autoexec_done;
1784 if( !db_exists("SELECT 1 FROM pending_alert") ) goto autoexec_done;
1785 email_send_alerts(0);
1786 iJulianDay = db_int(0, "SELECT julianday('now')");
 
1787 if( iJulianDay>db_get_int("email-last-digest",0) ){
1788 if( db_transaction_nesting_depth()!=1 ){
1789 fossil_warning("Transaction nesting error prior to digest processing");
1790 }else{
1791 db_set_int("email-last-digest",iJulianDay,0);
1792 email_send_alerts(SENDALERT_DIGEST);
1793 }
1794 }
1795
1796 autoexec_done:
1797 db_end_transaction(0);
1798 }
1799
+38
--- src/main.c
+++ src/main.c
@@ -2508,10 +2508,14 @@
25082508
#if defined(_WIN32)
25092509
const char *zStopperFile; /* Name of file used to terminate server */
25102510
zStopperFile = find_option("stopper", 0, 1);
25112511
#endif
25122512
2513
+ if( g.zErrlog==0 ){
2514
+ g.zErrlog = "-";
2515
+ g.fAnyTrace = 1;
2516
+ }
25132517
zFileGlob = find_option("files-urlenc",0,1);
25142518
if( zFileGlob ){
25152519
char *z = mprintf("%s", zFileGlob);
25162520
dehttpize(z);
25172521
zFileGlob = z;
@@ -2707,5 +2711,39 @@
27072711
}
27082712
fossil_print("]\n");
27092713
}
27102714
}
27112715
}
2716
+
2717
+/*
2718
+** WEBPAGE: test-warning
2719
+**
2720
+** Test error and warning log operation. This webpage is accessible to
2721
+** the administrator only.
2722
+**
2723
+** case=1 Issue a fossil_warning() while generating the page.
2724
+** case=2 Extra db_begin_transaction()
2725
+** case=3 Extra db_end_transaction()
2726
+*/
2727
+void test_warning_page(void){
2728
+ int iCase = atoi(PD("case","1"));
2729
+ login_check_credentials();
2730
+ if( !g.perm.Admin ){ fossil_redirect_home(); return; }
2731
+ style_header("Warning Test Page");
2732
+ @ <p>This is the test page for case=%d(iCase)</p>
2733
+ @ <ol>
2734
+ @ <li value='1'> Call fossil_warning()
2735
+ if( iCase==1 ){
2736
+ fossil_warning("Test warning message from /test-warning");
2737
+ }
2738
+ @ <li value='2'> Call db_begin_transaction()
2739
+ if( iCase==2 ){
2740
+ db_begin_transaction();
2741
+ }
2742
+ @ <li value='3'> Call db_end_transaction()
2743
+ if( iCase==3 ){
2744
+ db_end_transaction(0);
2745
+ }
2746
+ @ </ol>
2747
+ @ <p>End of test</p>
2748
+ style_footer();
2749
+}
27122750
--- src/main.c
+++ src/main.c
@@ -2508,10 +2508,14 @@
2508 #if defined(_WIN32)
2509 const char *zStopperFile; /* Name of file used to terminate server */
2510 zStopperFile = find_option("stopper", 0, 1);
2511 #endif
2512
 
 
 
 
2513 zFileGlob = find_option("files-urlenc",0,1);
2514 if( zFileGlob ){
2515 char *z = mprintf("%s", zFileGlob);
2516 dehttpize(z);
2517 zFileGlob = z;
@@ -2707,5 +2711,39 @@
2707 }
2708 fossil_print("]\n");
2709 }
2710 }
2711 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2712
--- src/main.c
+++ src/main.c
@@ -2508,10 +2508,14 @@
2508 #if defined(_WIN32)
2509 const char *zStopperFile; /* Name of file used to terminate server */
2510 zStopperFile = find_option("stopper", 0, 1);
2511 #endif
2512
2513 if( g.zErrlog==0 ){
2514 g.zErrlog = "-";
2515 g.fAnyTrace = 1;
2516 }
2517 zFileGlob = find_option("files-urlenc",0,1);
2518 if( zFileGlob ){
2519 char *z = mprintf("%s", zFileGlob);
2520 dehttpize(z);
2521 zFileGlob = z;
@@ -2707,5 +2711,39 @@
2711 }
2712 fossil_print("]\n");
2713 }
2714 }
2715 }
2716
2717 /*
2718 ** WEBPAGE: test-warning
2719 **
2720 ** Test error and warning log operation. This webpage is accessible to
2721 ** the administrator only.
2722 **
2723 ** case=1 Issue a fossil_warning() while generating the page.
2724 ** case=2 Extra db_begin_transaction()
2725 ** case=3 Extra db_end_transaction()
2726 */
2727 void test_warning_page(void){
2728 int iCase = atoi(PD("case","1"));
2729 login_check_credentials();
2730 if( !g.perm.Admin ){ fossil_redirect_home(); return; }
2731 style_header("Warning Test Page");
2732 @ <p>This is the test page for case=%d(iCase)</p>
2733 @ <ol>
2734 @ <li value='1'> Call fossil_warning()
2735 if( iCase==1 ){
2736 fossil_warning("Test warning message from /test-warning");
2737 }
2738 @ <li value='2'> Call db_begin_transaction()
2739 if( iCase==2 ){
2740 db_begin_transaction();
2741 }
2742 @ <li value='3'> Call db_end_transaction()
2743 if( iCase==3 ){
2744 db_end_transaction(0);
2745 }
2746 @ </ol>
2747 @ <p>End of test</p>
2748 style_footer();
2749 }
2750
+6 -2
--- src/printf.c
+++ src/printf.c
@@ -992,12 +992,16 @@
992992
va_list ap;
993993
static const char *const azEnv[] = { "HTTP_HOST", "HTTP_USER_AGENT",
994994
"PATH_INFO", "QUERY_STRING", "REMOTE_ADDR", "REQUEST_METHOD",
995995
"REQUEST_URI", "SCRIPT_NAME" };
996996
if( g.zErrlog==0 ) return;
997
- out = fossil_fopen(g.zErrlog, "a");
998
- if( out==0 ) return;
997
+ if( g.zErrlog[0]=='-' && g.zErrlog[1]==0 ){
998
+ out = stderr;
999
+ }else{
1000
+ out = fossil_fopen(g.zErrlog, "a");
1001
+ if( out==0 ) return;
1002
+ }
9991003
now = time(0);
10001004
pNow = gmtime(&now);
10011005
fprintf(out, "------------- %04d-%02d-%02d %02d:%02d:%02d UTC ------------\n",
10021006
pNow->tm_year+1900, pNow->tm_mon+1, pNow->tm_mday+1,
10031007
pNow->tm_hour, pNow->tm_min, pNow->tm_sec);
10041008
--- src/printf.c
+++ src/printf.c
@@ -992,12 +992,16 @@
992 va_list ap;
993 static const char *const azEnv[] = { "HTTP_HOST", "HTTP_USER_AGENT",
994 "PATH_INFO", "QUERY_STRING", "REMOTE_ADDR", "REQUEST_METHOD",
995 "REQUEST_URI", "SCRIPT_NAME" };
996 if( g.zErrlog==0 ) return;
997 out = fossil_fopen(g.zErrlog, "a");
998 if( out==0 ) return;
 
 
 
 
999 now = time(0);
1000 pNow = gmtime(&now);
1001 fprintf(out, "------------- %04d-%02d-%02d %02d:%02d:%02d UTC ------------\n",
1002 pNow->tm_year+1900, pNow->tm_mon+1, pNow->tm_mday+1,
1003 pNow->tm_hour, pNow->tm_min, pNow->tm_sec);
1004
--- src/printf.c
+++ src/printf.c
@@ -992,12 +992,16 @@
992 va_list ap;
993 static const char *const azEnv[] = { "HTTP_HOST", "HTTP_USER_AGENT",
994 "PATH_INFO", "QUERY_STRING", "REMOTE_ADDR", "REQUEST_METHOD",
995 "REQUEST_URI", "SCRIPT_NAME" };
996 if( g.zErrlog==0 ) return;
997 if( g.zErrlog[0]=='-' && g.zErrlog[1]==0 ){
998 out = stderr;
999 }else{
1000 out = fossil_fopen(g.zErrlog, "a");
1001 if( out==0 ) return;
1002 }
1003 now = time(0);
1004 pNow = gmtime(&now);
1005 fprintf(out, "------------- %04d-%02d-%02d %02d:%02d:%02d UTC ------------\n",
1006 pNow->tm_year+1900, pNow->tm_mon+1, pNow->tm_mday+1,
1007 pNow->tm_hour, pNow->tm_min, pNow->tm_sec);
1008

Keyboard Shortcuts

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